All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] auvirt: a new tool for reporting events related to virtual machines
@ 2011-12-15 15:56 Marcelo Cerri
  2011-12-20 18:18 ` Steve Grubb
  0 siblings, 1 reply; 21+ messages in thread
From: Marcelo Cerri @ 2011-12-15 15:56 UTC (permalink / raw)
  To: linux-audit; +Cc: gcwilson, bryntcor

This patch adds a new tool to extract information related to virtual machines
from the audit log files. It can output a summary with information about the
number of events found with details by type of record and operation. The tool
can also output the filtered records as found in the audit log.

Using the --avc option auvirt tries to correlate AVC records to the guests
based on its security context. It's also possible to select records related to
just one guest using the UUID or the guest name.

This tool is based on the proposal sent in the RFC:

https://www.redhat.com/archives/linux-audit/2011-November/msg00014.html

Signed-off-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com>
---
 audit.spec                      |    2 +
 configure.ac                    |    2 +-
 tools/Makefile.am               |    2 +-
 tools/auvirt/Makefile.am        |   34 +++
 tools/auvirt/auvirt-map-llist.c |  181 +++++++++++++
 tools/auvirt/auvirt-map.h       |   50 ++++
 tools/auvirt/auvirt.8           |  107 ++++++++
 tools/auvirt/auvirt.c           |  541 +++++++++++++++++++++++++++++++++++++++
 8 files changed, 917 insertions(+), 2 deletions(-)
 create mode 100644 tools/auvirt/Makefile.am
 create mode 100644 tools/auvirt/auvirt-map-llist.c
 create mode 100644 tools/auvirt/auvirt-map.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..4ee2ca7
--- /dev/null
+++ b/tools/auvirt/Makefile.am
@@ -0,0 +1,34 @@
+#
+# 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@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-map.h
+man_MANS = auvirt.8
+
+auvirt_SOURCES = auvirt.c auvirt-map-llist.c ${top_srcdir}/src/ausearch-time.c
diff --git a/tools/auvirt/auvirt-map-llist.c b/tools/auvirt/auvirt-map-llist.c
new file mode 100644
index 0000000..af55a41
--- /dev/null
+++ b/tools/auvirt/auvirt-map-llist.c
@@ -0,0 +1,181 @@
+/*
+ * auvirt-map-llist.c --
+ * 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@br.ibm.com>
+ */
+
+
+#include "auvirt-map.h"
+#include <stdlib.h>
+#include <string.h>
+
+struct _map_t {
+	map_it_t *head;
+	map_it_t *tail;
+};
+
+struct _map_it_t{
+	char *key;
+	char *val;
+	map_it_t *prev;
+	map_it_t *next;
+};
+
+map_t *map_new()
+{
+	map_t *m = malloc(sizeof(map_t));
+	if (m == NULL)
+		return NULL;
+	m->head = NULL;
+	m->tail = NULL;
+	return m;
+}
+
+void free_node(map_it_t *it)
+{
+	if (it) {
+		free(it->key);
+		free(it->val);
+		free(it);
+	}
+}
+
+void map_free(map_t *m)
+{
+	if (m != NULL) {
+		map_it_t *it = m->head;
+		while (it && it->next) {
+			it = it->next;
+			free_node(it->prev);
+		}
+		free_node(it);
+		free(m);
+	}
+}
+
+int map_add(map_t *m, const char *k, const char *v)
+{
+	map_it_t *it = NULL;
+	if (m == NULL || k == NULL || v == NULL || map_get(m, k) != NULL)
+		goto error;
+
+	it = malloc(sizeof(map_it_t));
+	if (it == NULL)
+		goto error;
+	memset(it, 0, sizeof(map_it_t));
+	it->key = strdup(k);
+	if (it->key == NULL)
+		goto error;
+	it->val = strdup(v);
+	if (it->val == NULL)
+		goto error;
+
+	if (m->tail == NULL) {
+		m->head = m->tail = it;
+		return 0;
+	}
+
+	it->prev = m->tail;
+	m->tail->next = it;
+	m->tail = it;
+	return 0;
+
+error:
+	free_node(it);
+	return 1;
+}
+
+int map_set(map_t *m, map_it_t *it, const char *v)
+{
+	char *str = NULL;
+	if (m == NULL || it == NULL || v == NULL)
+		return 1;
+	if (v) {
+		str = strdup(v);
+		if (str == NULL)
+			return 1;
+	}
+	free(it->val);
+	it->val = str;
+	return 0;
+}
+
+int map_del(map_t *m, map_it_t *it)
+{
+	if (m == NULL || it == NULL)
+		return 1;
+	if (m->head == it)
+		m->head = it->next;
+	if (m->tail == it)
+		m->tail = it->prev;
+	if (it->next)
+		it->next->prev = it->prev;
+	if (it->prev)
+		it->prev->next = it->next;
+	free_node(it);
+	return 0;
+}
+
+map_it_t *map_get(map_t *m, const char *k)
+{
+	map_it_t *it = NULL;
+	if (m == NULL || k == NULL)
+		return NULL;
+	for (it = m->head; it; it = it->next)
+		if (strcmp(it->key, k) == 0)
+			return it;
+	return NULL;
+}
+
+map_it_t *map_next(const map_t *m, map_it_t *last)
+{
+	if (m == NULL)
+		return NULL;
+	if (last == NULL)
+		return m->head;
+	return last->next;
+}
+
+map_it_t *map_next_val(const map_t *m, const char *v, map_it_t *last)
+{
+	map_it_t *it = NULL;
+	if (m == NULL)
+		return NULL;
+	it = (last) ? last->next : m->head;
+	for (; it; it = it->next)
+		if (strcmp(it->val, v) == 0)
+			return it;
+	return NULL;
+}
+
+const char *map_it_key(const map_it_t *it)
+{
+	if (it)
+		return it->key;
+	return NULL;
+}
+
+const char *map_it_val(const map_it_t *it)
+{
+	if (it)
+		return it->val;
+	return NULL;
+}
+
diff --git a/tools/auvirt/auvirt-map.h b/tools/auvirt/auvirt-map.h
new file mode 100644
index 0000000..92fdb1a
--- /dev/null
+++ b/tools/auvirt/auvirt-map.h
@@ -0,0 +1,50 @@
+/*
+ * auvirt-map.h --
+ * 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@br.ibm.com>
+ */
+
+
+#ifndef AUVIRTMAP_HEADER
+#define AUVIRTMAP_HEADER
+
+struct _map_t;
+typedef struct _map_t map_t;
+struct _map_it_t;
+typedef struct _map_it_t map_it_t;
+
+map_t *map_new();
+void map_free(map_t *m);
+int map_add(map_t *m, const char *k, const char *v);
+int map_set(map_t *m, map_it_t *it, const char *v);
+int map_del(map_t *m, map_it_t *it);
+map_it_t *map_get(map_t *m, const char *k);
+map_it_t *map_next(const map_t *m, map_it_t *last);
+map_it_t *map_next_val(const map_t *m, const char *v, map_it_t *last);
+const char *map_it_key(const map_it_t *it);
+const char *map_it_val(const map_it_t *it);
+
+static inline const char *map_get_val(map_t *m, const char *k)
+{
+	return map_it_val(map_get(m, k));
+}
+
+#endif
+
diff --git a/tools/auvirt/auvirt.8 b/tools/auvirt/auvirt.8
new file mode 100644
index 0000000..1fc629b
--- /dev/null
+++ b/tools/auvirt/auvirt.8
@@ -0,0 +1,107 @@
+.TH AUVIRT 8 "Dec 2011" "IBM Corp" "System Administration Utilities"
+.SH NAME
+auvirt - a program that reports data related to virtual machines
+
+.SH SYNOPSIS
+.B auvirt
+[ \fIOPTIONS\fP ]
+[ \fIUUID\fP | \fIVM-NAME\fP ]
+
+.SH DESCRIPTION
+\fBauvirt\fP is a program that prints a summary of all virtualization related
+events 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.
+
+The summary contains the number of virtualization related events
+found, the number of events by type (AVC, machine id, control and resource),
+and the number of start, stop and suspend operations found.
+
+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-a\fP, \fB--avc\fP
+When this option is given the tool tries to correlate AVC records to a
+guest based on the security context. As the security context can be
+automatically changed on guest's start, the tool just correlates an AVC record
+to a guest if the record was generated during the period time that guest was
+running. This option causes auvirt to count AVC records and show the result in
+the summary. If \fB--raw\fP is specified together with this option, the AVC
+records considered
+will be printed.
+.TP
+\fB--debug\fP
+Print debug messages to standard output.
+.TP
+\fB-f\fP, \fB--file\fP \fIfile\fP
+Reads 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--raw\fP
+Print to standard output the filtered records as shown in the audit log.
+.TP
+\fB--stdin\fP
+Reads records from the standard input instead from the system audit log file.
+This option cannot be informed with \fB--file\fP.
+.TP
+\fB--summary\fP
+Prints a summary with information about the records found. This is the default
+behavior.
+.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.
+
+.SH EXAMPLES
+To see all the records in this month for a guest
+
+\fBauvirt --raw --avc --start this-month GuestVmName\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..3e5b630
--- /dev/null
+++ b/tools/auvirt/auvirt.c
@@ -0,0 +1,541 @@
+/*
+ * 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@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 "auparse.h"
+#include "libaudit.h"
+#include "auvirt-map.h"
+#include "ausearch-time.h"
+
+/* command line parameters */
+static int help_flag = 0;
+static int stdin_flag = 0;
+static int raw_flag = 0;
+static int summary_flag = 0;
+static int avc_flag = 0;
+/* id stores the given UUID or VM name. */
+static const char *id = NULL;
+static const char *file = NULL;
+static enum { UUID=0, VMNAME } id_type;
+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;
+ */
+
+/* state variables */
+static map_t *seclevel_map = NULL;
+static struct {
+	long n_records;
+	long n_virt_records;
+	long n_res_records;
+	long n_mach_id_records;
+	long n_avc_records;
+	long n_errors;
+	time_t start_time;
+	time_t end_time;
+	struct {
+		long n_start;
+		long n_stop;
+	} by_type;
+} summary =  { 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, { 0L, 0L } };
+
+char *rstrip(char *s)
+{
+	size_t i;
+	if (s) {
+		for (i = 0; s[i] && s[i + 1]; i++);
+		for (; isspace(s[i]); i--) {
+			s[i] = 0;
+			if (i == 0)
+				break;
+		}
+	}
+	return s;
+}
+
+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 *arg = argv[i];
+		if (arg[0] != '-') {
+			/* parse UUID or VM name */
+			if (id != NULL) {
+				fprintf(stderr, "Only one UUID or VM name "
+						"must be specified.\n");
+				goto error;
+			}
+			if (regexec(&uuid_regex, arg, 0, NULL, 0) == 0) {
+				id_type = UUID;
+			} else {
+				id_type = VMNAME;
+			}
+			id = arg;
+		} else if (strcmp("--raw", arg) == 0 ||
+			   strcmp("-r", arg) == 0) {
+			raw_flag = 1;
+		} else if (strcmp("--summary", arg) == 0 ||
+			   strcmp("-s", arg) == 0) {
+			summary_flag = 1;
+		} else if (strcmp("--file", arg) == 0 ||
+			   strcmp("-f", arg) == 0) {
+			if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+				fprintf(stderr, "\"%s\" option requires "
+						"an argument.\n", arg);
+				goto error;
+			}
+			file = argv[++i];
+		} else if (strcmp("--stdin", arg) == 0) {
+			stdin_flag = 1;
+		} else if (strcmp("--help", arg) == 0 ||
+			   strcmp("-h", arg) == 0) {
+			help_flag = 1;
+			goto exit;
+		} else if (strcmp("--start", arg) == 0 ||
+			   strcmp("-ts", arg) == 0) {
+			const char *date, *time = NULL;
+			if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+				fprintf(stderr, "\"%s\" option requires at "
+						"least one argument.\n", arg);
+				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", arg) == 0 ||
+			   strcmp("-te", arg) == 0) {
+			const char *date, *time = NULL;
+			if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+				fprintf(stderr, "\"%s\" option requires at "
+						"least one argument.\n", arg);
+				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("--avc", arg) == 0 ||
+			   strcmp("-a", arg) == 0) {
+			avc_flag = 1;
+		} else if (strcmp("--debug", arg) == 0) {
+			debug = 1;
+		} else {
+			fprintf(stderr, "Unknown option \"%s\".\n", arg);
+			goto error;
+		}
+	}
+
+	/* validate conflicting options */
+	if (stdin_flag && file) {
+		fprintf(stderr, "\"--sdtin\" and \"--file\" options "
+				"must not be specified together.\n");
+		goto error;
+	}
+	if (raw_flag && summary_flag) {
+		fprintf(stderr, "\"--raw\" and \"--summary\" options "
+				"must not be specified together.\n");
+		goto error;
+	}
+
+	/* summary is default */
+	if (!raw_flag && !summary_flag)
+		summary_flag = 1;
+
+	if (debug) {
+		fprintf(stdout, "help_flag='%i'\n", help_flag);
+		fprintf(stdout, "stdin_flag='%i'\n", stdin_flag);
+		fprintf(stdout, "raw_flag='%i'\n", raw_flag);
+		fprintf(stdout, "summary_flag='%i'\n", summary_flag);
+		fprintf(stdout, "avc_flag='%i'\n", avc_flag);
+		fprintf(stdout, "id='%s'\n", id);
+		fprintf(stdout, "file='%s'\n", file );
+		fprintf(stdout, "id_type='%i'\n", id_type);
+		fprintf(stdout, "start_time='%s'\n", (start_time == 0L) ?
+				"" : rstrip(ctime(&start_time)));
+		fprintf(stdout, "end_time='%s'\n", (end_time == 0L) ?
+				"" : rstrip(ctime(&end_time)));
+	}
+
+exit:
+	regfree(&uuid_regex);
+	return rc;
+error:
+	rc = 1;
+	goto exit;
+}
+
+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];
+	sprintf(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 (id) {
+		char tmp[1024];
+		char *field_name = (id_type == UUID) ? "uuid" : "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...
+		 */
+		if (id_type == VMNAME)
+			snprintf(tmp, sizeof(tmp), "\"%s\"", id);
+		if (ausearch_add_item(au, field_name, "=", tmp,
+					AUSEARCH_RULE_AND)) {
+			fprintf(stderr, "Criteria error: id\n");
+			return 1;
+		}
+	}
+	if (avc_flag) {
+		if (ausearch_add_item(au, "type", "=", "AVC",
+					AUSEARCH_RULE_OR)) {
+			fprintf(stderr, "Criteria error: AVC\n");
+			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;
+}
+
+void usage(FILE *output)
+{
+	fprintf(output, "usage: auvirt [--stdin] [--avc] [--raw|--summary] "
+			"[--start start-date [start-time]] "
+			"[--end end-date [end-time]] [--file file-name] "
+			"[uuid|vm-name]\n");
+}
+
+/* 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;
+}
+
+/* keep track of which security level is associated to each uuid */
+int update_seclevel(auparse_state_t *au)
+{
+	const char *uuid, *seclabel, *seclevel;
+
+	uuid = auparse_find_field(au, "uuid");
+	if (uuid == NULL) {
+		if (debug)
+			fprintf(stdout, "Warning: record without UUID\n");
+		return 0;
+	}
+
+	seclabel = auparse_find_field(au, "vm-ctx");
+	seclevel = get_seclevel(seclabel);
+	if (seclevel == NULL) {
+		if (debug)
+			fprintf(stdout, "Invalid security level for label: "
+					"'%s'.\n", seclabel);
+		return 0;
+	}
+	if (debug)
+		fprintf(stdout, "Using seclevel '%s' for '%s'\n",
+				seclevel, uuid);
+
+	map_it_t *it = map_get(seclevel_map, uuid);
+	if (it) {
+		if (debug)
+			fprintf(stdout, "Warning: overwriting seclevel for id "
+					"'%s'\n", uuid);
+		return map_set(seclevel_map, it, seclevel);
+	}
+	return map_add(seclevel_map, uuid, seclevel);
+}
+
+/* check and discard sec level associated to a stopped guest. */
+int remove_seclevel(auparse_state_t *au)
+{
+	const char *uuid, *op;
+
+	uuid = auparse_find_field(au, "uuid");
+	if (uuid == NULL) {
+		if (debug)
+			fprintf(stdout, "Warning: record without UUID\n");
+		return 0;
+	}
+
+	auparse_first_field(au);
+	op = auparse_find_field(au, "op");
+	if (op == NULL) {
+		if (debug)
+			fprintf(stdout, "Warning: record without 'op' "
+					"field\n");
+		return 0;
+	}
+	if (strcmp("stop", op) == 0) {
+		map_it_t *it = map_get(seclevel_map, uuid);
+		if (it) {
+			if (debug)
+				fprintf(stdout, "Discarding seclevel '%s'\n",
+						map_it_val(it));
+			map_del(seclevel_map, it);
+		} else if (debug)
+			fprintf(stdout, "No seclevel to discard\n");
+	}
+	return 0;
+}
+
+void process_record(auparse_state_t *au)
+{
+	if (raw_flag)
+		printf("%s\n", auparse_get_record_text(au));
+
+	if (summary_flag) {
+		const char *op;
+		int type = auparse_get_type(au);
+		switch (type) {
+		case AUDIT_FIRST_VIRT_MSG ... AUDIT_LAST_VIRT_MSG:
+			summary.n_virt_records += 1;
+			/* update time period from records if not informed */
+			time_t t = auparse_get_time(au);
+			if (summary.start_time == 0L ||
+			    summary.start_time > t) {
+				summary.start_time = t;
+			}
+			if (summary.end_time == 0L ||
+			    summary.end_time < t) {
+				summary.end_time = t;
+			}
+			/* count start and stop */
+			switch (type) {
+			case AUDIT_VIRT_RESOURCE:
+				summary.n_res_records += 1;
+				break;
+			case AUDIT_VIRT_MACHINE_ID:
+				summary.n_mach_id_records += 1;
+				break;
+			case AUDIT_VIRT_CONTROL:
+				op = auparse_find_field(au, "op");
+				if (strcmp("start", op) == 0)
+					summary.by_type.n_start += 1;
+				else if (strcmp("stop", op) == 0)
+					summary.by_type.n_stop += 1;
+				break;
+			}
+			break;
+		case AUDIT_AVC:
+			summary.n_avc_records += 1;
+			break;
+		}
+		summary.n_records += 1;
+	}
+}
+
+void print_summary()
+{
+	time_t time = 0;
+	printf("Total records:      %ld\n", summary.n_records);
+	printf("Virt records:       %ld\n", summary.n_virt_records);
+	printf("Resource records:   %ld\n", summary.n_res_records);
+	printf("Machine ID records: %ld\n", summary.n_mach_id_records);
+	printf("AVC records:        %ld\n", summary.n_avc_records);
+	printf("Operations:\n");
+	printf("  Start:            %ld\n", summary.by_type.n_start);
+	printf("  Stop:             %ld\n", summary.by_type.n_stop);
+	printf("Considered time:\n");
+	time = (start_time) ? start_time : summary.start_time;
+	printf("  Start:            %s\n", (time) ? rstrip(ctime(&time)) : "-");
+	time = (end_time) ? end_time : summary.end_time;
+	printf("  End:              %s\n", (time) ? rstrip(ctime(&time)) : "-");
+}
+
+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 auparse */
+	au = init_auparse();
+	if (au == NULL)
+		goto error;
+	if (create_search_criteria(au))
+		goto error;
+
+	if (avc_flag) {
+		seclevel_map = map_new();
+		if (seclevel_map == NULL)
+			goto unexpected_error;
+	}
+
+	while (ausearch_next_event(au) > 0) {
+		const char *seclabel = NULL, *seclevel = NULL;
+		int type = auparse_get_type(au);
+		switch (type) {
+		case AUDIT_FIRST_VIRT_MSG ... AUDIT_LAST_VIRT_MSG:
+			/* when the --avc option is given the tool will keep
+			 * track of the security label associated to each guest
+			 * to be able to filter the AVC records. */
+			if (avc_flag) {
+				if (type == AUDIT_VIRT_MACHINE_ID) {
+					/* When a start event is read, an entry
+					 * is added or update in a map for that
+					 * guest. */
+					if (update_seclevel(au))
+						goto unexpected_error;
+				} else if (type == AUDIT_VIRT_CONTROL) {
+					/* When a stop event is read, the entry
+					 * associated to that guest is removed
+					 * from the map. */
+					if (remove_seclevel(au))
+						goto unexpected_error;
+				}
+			}
+			process_record(au);
+			break;
+		case AUDIT_AVC:
+			/* process AVC records with security level used by
+			 * a known guest. */
+			if (avc_flag) {
+				seclabel = auparse_find_field(au, "tcontext");
+				seclevel = get_seclevel(seclabel);
+			}
+			if (seclevel) {
+				if (map_next_val(seclevel_map, seclevel, NULL))
+					process_record(au);
+			}
+			break;
+		}
+		auparse_next_event(au);
+	}
+
+	if (summary_flag)
+		print_summary();
+
+exit:
+	if (au)
+		auparse_destroy(au);
+	map_free(seclevel_map);
+	seclevel_map = NULL;
+	if (debug)
+		fprintf(stdout, "Exit code: %d\n", rc);
+	return rc;
+
+unexpected_error:
+	fprintf(stderr, "Unexpected error\n");
+error:
+	rc = 1;
+	goto exit;
+}
+
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2011-12-15 15:56 [PATCH] auvirt: a new tool for reporting events related to virtual machines Marcelo Cerri
@ 2011-12-20 18:18 ` Steve Grubb
  2012-01-05 16:44   ` Marcelo Cerri
  0 siblings, 1 reply; 21+ messages in thread
From: Steve Grubb @ 2011-12-20 18:18 UTC (permalink / raw)
  To: linux-audit; +Cc: gcwilson, bryntcor

On Thursday, December 15, 2011 10:56:51 AM Marcelo Cerri wrote:
> This patch adds a new tool to extract information related to virtual
> machines from the audit log files. It can output a summary with
> information about the number of events found with details by type of
> record and operation. The tool can also output the filtered records as
> found in the audit log.
> 
> Using the --avc option auvirt tries to correlate AVC records to the guests
> based on its security context. It's also possible to select records related
> to just one guest using the UUID or the guest name.

I'm wondering about this tool. It runs fine. But I thought you were wanting to do 
some more sophisticated analysis of events. For example this is the current 
output:

$ ./auvirt --file ../../../virt-audit.log
Total records:      6
Virt records:       6
Resource records:   4
Machine ID records: 1
AVC records:        0
Operations:
  Start:            1
  Stop:             0
Considered time:
  Start:            Tue Dec 20 09:33:01 2011
  End:              Tue Dec 20 09:33:01 2011

This is not much different than what can be reported by ausearch/report with the 
new uuid and vm search fields. Also, testing with the uuid number doesn't seem to 
get any hits. But using the vm name does. 

I plan to add a very basic virt report to aureport soon. I was wondering if the 
above is all anyone really wanted to see? I would think that perhaps you want 
some info about start/stop assignment of resources, changes in resources, and 
perhaps MAC or anomaly events related to a vm. But laid out like the aulast 
program.

boot  vm-name   time  (total runtime)
resource  what-kind  old-value  new-value  time (total time assigned)
avc   access-type  obj  results  time
shutdown  vm-name  time

and there might be other audit events associated with a vm.

-Steve

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2011-12-20 18:18 ` Steve Grubb
@ 2012-01-05 16:44   ` Marcelo Cerri
  2012-01-09 17:00     ` Marcelo Cerri
  2012-01-11 21:20     ` Steve Grubb
  0 siblings, 2 replies; 21+ messages in thread
From: Marcelo Cerri @ 2012-01-05 16:44 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit, gcwilson, bryntcor

Hi Steve,

Thanks for you feedback.

I'm already updating the source code based on your comments and looking 
for another events that may be correlated to a VM.

But I'm not sure what means "anomaly events". Would it be malformed 
records (without some fields, for example) or a specific record type 
generated by the kernel or some other userspace application?

Regards,
Marcelo

On 12/20/2011 04:18 PM, Steve Grubb wrote:
> On Thursday, December 15, 2011 10:56:51 AM Marcelo Cerri wrote:
>> This patch adds a new tool to extract information related to virtual
>> machines from the audit log files. It can output a summary with
>> information about the number of events found with details by type of
>> record and operation. The tool can also output the filtered records as
>> found in the audit log.
>>
>> Using the --avc option auvirt tries to correlate AVC records to the guests
>> based on its security context. It's also possible to select records related
>> to just one guest using the UUID or the guest name.
> I'm wondering about this tool. It runs fine. But I thought you were wanting to do
> some more sophisticated analysis of events. For example this is the current
> output:
>
> $ ./auvirt --file ../../../virt-audit.log
> Total records:      6
> Virt records:       6
> Resource records:   4
> Machine ID records: 1
> AVC records:        0
> Operations:
>    Start:            1
>    Stop:             0
> Considered time:
>    Start:            Tue Dec 20 09:33:01 2011
>    End:              Tue Dec 20 09:33:01 2011
>
> This is not much different than what can be reported by ausearch/report with the
> new uuid and vm search fields. Also, testing with the uuid number doesn't seem to
> get any hits. But using the vm name does.
>
> I plan to add a very basic virt report to aureport soon. I was wondering if the
> above is all anyone really wanted to see? I would think that perhaps you want
> some info about start/stop assignment of resources, changes in resources, and
> perhaps MAC or anomaly events related to a vm. But laid out like the aulast
> program.
>
> boot  vm-name   time  (total runtime)
> resource  what-kind  old-value  new-value  time (total time assigned)
> avc   access-type  obj  results  time
> shutdown  vm-name  time
>
> and there might be other audit events associated with a vm.
>
> -Steve
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-05 16:44   ` Marcelo Cerri
@ 2012-01-09 17:00     ` Marcelo Cerri
  2012-01-11 21:48       ` Steve Grubb
  2012-01-11 21:20     ` Steve Grubb
  1 sibling, 1 reply; 21+ messages in thread
From: Marcelo Cerri @ 2012-01-09 17:00 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit, gcwilson, bryntcor

Just another question.

Currently, auvirt has two different modes defined by the options 
"--summary" and "--raw". In your last email, you suggested that summary 
would be laid out like the aulast program. Do you think that would be a 
good idea to have a option to output all the matched records, as in 
"--raw", but using a layout similar to aulast too?

Regards,
Marcelo

On 01/05/2012 02:44 PM, Marcelo Cerri wrote:
> Hi Steve,
>
> Thanks for you feedback.
>
> I'm already updating the source code based on your comments and 
> looking for another events that may be correlated to a VM.
>
> But I'm not sure what means "anomaly events". Would it be malformed 
> records (without some fields, for example) or a specific record type 
> generated by the kernel or some other userspace application?
>
> Regards,
> Marcelo
>
> On 12/20/2011 04:18 PM, Steve Grubb wrote:
>> On Thursday, December 15, 2011 10:56:51 AM Marcelo Cerri wrote:
>>> This patch adds a new tool to extract information related to virtual
>>> machines from the audit log files. It can output a summary with
>>> information about the number of events found with details by type of
>>> record and operation. The tool can also output the filtered records as
>>> found in the audit log.
>>>
>>> Using the --avc option auvirt tries to correlate AVC records to the 
>>> guests
>>> based on its security context. It's also possible to select records 
>>> related
>>> to just one guest using the UUID or the guest name.
>> I'm wondering about this tool. It runs fine. But I thought you were 
>> wanting to do
>> some more sophisticated analysis of events. For example this is the 
>> current
>> output:
>>
>> $ ./auvirt --file ../../../virt-audit.log
>> Total records:      6
>> Virt records:       6
>> Resource records:   4
>> Machine ID records: 1
>> AVC records:        0
>> Operations:
>>    Start:            1
>>    Stop:             0
>> Considered time:
>>    Start:            Tue Dec 20 09:33:01 2011
>>    End:              Tue Dec 20 09:33:01 2011
>>
>> This is not much different than what can be reported by 
>> ausearch/report with the
>> new uuid and vm search fields. Also, testing with the uuid number 
>> doesn't seem to
>> get any hits. But using the vm name does.
>>
>> I plan to add a very basic virt report to aureport soon. I was 
>> wondering if the
>> above is all anyone really wanted to see? I would think that perhaps 
>> you want
>> some info about start/stop assignment of resources, changes in 
>> resources, and
>> perhaps MAC or anomaly events related to a vm. But laid out like the 
>> aulast
>> program.
>>
>> boot  vm-name   time  (total runtime)
>> resource  what-kind  old-value  new-value  time (total time assigned)
>> avc   access-type  obj  results  time
>> shutdown  vm-name  time
>>
>> and there might be other audit events associated with a vm.
>>
>> -Steve
>>
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-05 16:44   ` Marcelo Cerri
  2012-01-09 17:00     ` Marcelo Cerri
@ 2012-01-11 21:20     ` Steve Grubb
  2012-01-24 18:08       ` Marcelo Cerri
  1 sibling, 1 reply; 21+ messages in thread
From: Steve Grubb @ 2012-01-11 21:20 UTC (permalink / raw)
  To: Marcelo Cerri; +Cc: linux-audit, gcwilson, bryntcor

On Thursday, January 05, 2012 11:44:57 AM Marcelo Cerri wrote:
> But I'm not sure what means "anomaly events". Would it be malformed
> records (without some fields, for example) or a specific record type
> generated by the kernel or some other userspace application?

No, these are events in the range of AUDIT_FIRST_ANOM_MSG and 
AUDIT_LAST_ANOM_MSG and some from the kernel in the range of 
AUDIT_FIRST_KERN_ANOM_MSG and AUDIT_LAST_KERN_ANOM_MSG.

-Steve

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-09 17:00     ` Marcelo Cerri
@ 2012-01-11 21:48       ` Steve Grubb
  2012-01-13 17:25         ` Marcelo Cerri
  0 siblings, 1 reply; 21+ messages in thread
From: Steve Grubb @ 2012-01-11 21:48 UTC (permalink / raw)
  To: Marcelo Cerri; +Cc: linux-audit, gcwilson, bryntcor

On Monday, January 09, 2012 12:00:32 PM Marcelo Cerri wrote:
> Just another question.
> 
> Currently, auvirt has two different modes defined by the options
> "--summary" and "--raw". In your last email, you suggested that summary
> would be laid out like the aulast program.

Yeah, I was thinking of something like a timeline so that you can what happened 
to resources and in what order. It just so happens aulast is also a time line of 
system boots and logins. When it comes to a virt guest, I would want to see it 
boot, things assigned, things removed, anything funny happening to it, and then 
it shutting down. I also think the host being booted/shutdown might ought to be 
in there, too.


> Do you think that would be a good idea to have a option to output all the
> matched records, as in "--raw", but using a layout similar to aulast too?

I think you want both a concise report and the ability to pull the just records 
that made up the report. Aulast does this by having a proof mode that instead of 
giving you the records, it tells you how to pull them with ausearch.

-Steve


> On 01/05/2012 02:44 PM, Marcelo Cerri wrote:
> > Hi Steve,
> > 
> > Thanks for you feedback.
> > 
> > I'm already updating the source code based on your comments and
> > looking for another events that may be correlated to a VM.
> > 
> > But I'm not sure what means "anomaly events". Would it be malformed
> > records (without some fields, for example) or a specific record type
> > generated by the kernel or some other userspace application?
> > 
> > Regards,
> > Marcelo
> > 
> > On 12/20/2011 04:18 PM, Steve Grubb wrote:
> >> On Thursday, December 15, 2011 10:56:51 AM Marcelo Cerri wrote:
> >>> This patch adds a new tool to extract information related to virtual
> >>> machines from the audit log files. It can output a summary with
> >>> information about the number of events found with details by type of
> >>> record and operation. The tool can also output the filtered records as
> >>> found in the audit log.
> >>> 
> >>> Using the --avc option auvirt tries to correlate AVC records to the
> >>> guests
> >>> based on its security context. It's also possible to select records
> >>> related
> >>> to just one guest using the UUID or the guest name.
> >> 
> >> I'm wondering about this tool. It runs fine. But I thought you were
> >> wanting to do
> >> some more sophisticated analysis of events. For example this is the
> >> current
> >> output:
> >> 
> >> $ ./auvirt --file ../../../virt-audit.log
> >> Total records:      6
> >> Virt records:       6
> >> Resource records:   4
> >> Machine ID records: 1
> >> AVC records:        0
> >> 
> >> Operations:
> >>    Start:            1
> >>    Stop:             0
> >> 
> >> Considered time:
> >>    Start:            Tue Dec 20 09:33:01 2011
> >>    End:              Tue Dec 20 09:33:01 2011
> >> 
> >> This is not much different than what can be reported by
> >> ausearch/report with the
> >> new uuid and vm search fields. Also, testing with the uuid number
> >> doesn't seem to
> >> get any hits. But using the vm name does.
> >> 
> >> I plan to add a very basic virt report to aureport soon. I was
> >> wondering if the
> >> above is all anyone really wanted to see? I would think that perhaps
> >> you want
> >> some info about start/stop assignment of resources, changes in
> >> resources, and
> >> perhaps MAC or anomaly events related to a vm. But laid out like the
> >> aulast
> >> program.
> >> 
> >> boot  vm-name   time  (total runtime)
> >> resource  what-kind  old-value  new-value  time (total time assigned)
> >> avc   access-type  obj  results  time
> >> shutdown  vm-name  time
> >> 
> >> and there might be other audit events associated with a vm.
> >> 
> >> -Steve

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-11 21:48       ` Steve Grubb
@ 2012-01-13 17:25         ` Marcelo Cerri
  2012-01-13 19:23           ` Steve Grubb
  0 siblings, 1 reply; 21+ messages in thread
From: Marcelo Cerri @ 2012-01-13 17:25 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit, gcwilson, bryntcor

Hi,

These are some output examples of auvirt. What do you think?

I just added a "--full" option because libvirt can generate several 
resource events and this can make the output confusing.

Regards,
Marcelo

------

$ ./auvirt
start guest-name-1    root    Tue Jan 10 11:05
stop  guest-name-1    root    Tue Jan 10 11:39
start guest-name-2    root    Wed Jan 11 15:23
start guest-name-2    root    Wed Jan 11 16:28
start guest-name-1    root    Wed Jan 12 19:47

$ ./auvirt --show-uuid
start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan 
10 11:05
stop  guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan 
10 11:39
start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan 
11 15:23
start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan 
11 16:28
start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Wed Jan 
12 19:47

$ ./auvirt --summary # keep the same behaviour

$ ./auvirt --uuid fb4149f5-9ff6-4095-f6d3-a1d03936fdfa
start guest-name-1    root    Tue Jan 10 11:05
stop  guest-name-1    root    Tue Jan 10 11:39
start guest-name-1    root    Wed Jan 12 19:47

$ ./auvirt --vm-name guest-name-2
start guest-name-2    root    Wed Jan 11 15:23
start guest-name-2    root    Wed Jan 11 16:28

$ ./auvirt --full --uuid f937029b-93ca-4e13-b40b-663f46323503
res   guest-name-2    root    Wed Jan 11 15:23    disk    "?"    
"/images/guest-2.img"
res   guest-name-2    root    Wed Jan 11 15:23    vcpu    "0"    "4"
res   guest-name-2    root    Wed Jan 11 15:23    net     "?"    
"52:54:00:DB:AE:B4"
res   guest-name-2    root    Wed Jan 11 15:23    mem     "?"    "1048576"
start guest-name-2    root    Wed Jan 11 15:23
avc   guest-name-2    root    Wed Jan 11 19:49    read    
"/images/guest-2.img"    denied
res   guest-name-2    root    Wed Jan 11 15:23    mem     "1048576"    
"2097152"
stop  guest-name-2    root    Wed Jan 11 16:28

$ ./auvirt --full # same as above but showing events related to all guests


On 01/11/2012 07:48 PM, Steve Grubb wrote:
> On Monday, January 09, 2012 12:00:32 PM Marcelo Cerri wrote:
>> Just another question.
>>
>> Currently, auvirt has two different modes defined by the options
>> "--summary" and "--raw". In your last email, you suggested that summary
>> would be laid out like the aulast program.
>
> Yeah, I was thinking of something like a timeline so that you can what happened
> to resources and in what order. It just so happens aulast is also a time line of
> system boots and logins. When it comes to a virt guest, I would want to see it
> boot, things assigned, things removed, anything funny happening to it, and then
> it shutting down. I also think the host being booted/shutdown might ought to be
> in there, too.
>
>
>> Do you think that would be a good idea to have a option to output all the
>> matched records, as in "--raw", but using a layout similar to aulast too?
>
> I think you want both a concise report and the ability to pull the just records
> that made up the report. Aulast does this by having a proof mode that instead of
> giving you the records, it tells you how to pull them with ausearch.
>
> -Steve
>
>
>> On 01/05/2012 02:44 PM, Marcelo Cerri wrote:
>>> Hi Steve,
>>>
>>> Thanks for you feedback.
>>>
>>> I'm already updating the source code based on your comments and
>>> looking for another events that may be correlated to a VM.
>>>
>>> But I'm not sure what means "anomaly events". Would it be malformed
>>> records (without some fields, for example) or a specific record type
>>> generated by the kernel or some other userspace application?
>>>
>>> Regards,
>>> Marcelo
>>>
>>> On 12/20/2011 04:18 PM, Steve Grubb wrote:
>>>> On Thursday, December 15, 2011 10:56:51 AM Marcelo Cerri wrote:
>>>>> This patch adds a new tool to extract information related to virtual
>>>>> machines from the audit log files. It can output a summary with
>>>>> information about the number of events found with details by type of
>>>>> record and operation. The tool can also output the filtered records as
>>>>> found in the audit log.
>>>>>
>>>>> Using the --avc option auvirt tries to correlate AVC records to the
>>>>> guests
>>>>> based on its security context. It's also possible to select records
>>>>> related
>>>>> to just one guest using the UUID or the guest name.
>>>>
>>>> I'm wondering about this tool. It runs fine. But I thought you were
>>>> wanting to do
>>>> some more sophisticated analysis of events. For example this is the
>>>> current
>>>> output:
>>>>
>>>> $ ./auvirt --file ../../../virt-audit.log
>>>> Total records:      6
>>>> Virt records:       6
>>>> Resource records:   4
>>>> Machine ID records: 1
>>>> AVC records:        0
>>>>
>>>> Operations:
>>>>     Start:            1
>>>>     Stop:             0
>>>>
>>>> Considered time:
>>>>     Start:            Tue Dec 20 09:33:01 2011
>>>>     End:              Tue Dec 20 09:33:01 2011
>>>>
>>>> This is not much different than what can be reported by
>>>> ausearch/report with the
>>>> new uuid and vm search fields. Also, testing with the uuid number
>>>> doesn't seem to
>>>> get any hits. But using the vm name does.
>>>>
>>>> I plan to add a very basic virt report to aureport soon. I was
>>>> wondering if the
>>>> above is all anyone really wanted to see? I would think that perhaps
>>>> you want
>>>> some info about start/stop assignment of resources, changes in
>>>> resources, and
>>>> perhaps MAC or anomaly events related to a vm. But laid out like the
>>>> aulast
>>>> program.
>>>>
>>>> boot  vm-name   time  (total runtime)
>>>> resource  what-kind  old-value  new-value  time (total time assigned)
>>>> avc   access-type  obj  results  time
>>>> shutdown  vm-name  time
>>>>
>>>> and there might be other audit events associated with a vm.
>>>>
>>>> -Steve
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-13 17:25         ` Marcelo Cerri
@ 2012-01-13 19:23           ` Steve Grubb
  2012-01-13 19:45             ` Marcelo Cerri
  2012-01-16 13:05             ` Marcelo Cerri
  0 siblings, 2 replies; 21+ messages in thread
From: Steve Grubb @ 2012-01-13 19:23 UTC (permalink / raw)
  To: Marcelo Cerri; +Cc: linux-audit, gcwilson, bryntcor

Hello,

On Friday, January 13, 2012 12:25:05 PM Marcelo Cerri wrote:
> These are some output examples of auvirt. What do you think?

I think you are on the right track.


> I just added a "--full" option because libvirt can generate several
> resource events and this can make the output confusing.

Hmm. Why not call it --resource if its a resource specific report? Full to me 
implies everything for all guests.


> $ ./auvirt
> start guest-name-1    root    Tue Jan 10 11:05
> stop  guest-name-1    root    Tue Jan 10 11:39
> start guest-name-2    root    Wed Jan 11 15:23
> start guest-name-2    root    Wed Jan 11 16:28
> start guest-name-1    root    Wed Jan 12 19:47

Why not collapse these into 1 line like last that shows a duration?

start guest-name-1    root    Tue Jan 10 11:05 - 11:39 (00:34)

Do you have any samples for when a guest is paused and restarted? I would also 
collapse those into a line showing the duration of the pause.

pause guest-name-1    root    Tue Jan 10 11:15 - 11:30 (00:15)


> $ ./auvirt --show-uuid
> start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan
> 10 11:05
> stop  guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan
> 10 11:39
> start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan
> 11 15:23
> start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan
> 11 16:28
> start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Wed Jan
> 12 19:47
> 
> $ ./auvirt --summary # keep the same behaviour
> 
> $ ./auvirt --uuid fb4149f5-9ff6-4095-f6d3-a1d03936fdfa
> start guest-name-1    root    Tue Jan 10 11:05
> stop  guest-name-1    root    Tue Jan 10 11:39
> start guest-name-1    root    Wed Jan 12 19:47
> 
> $ ./auvirt --vm-name guest-name-2
> start guest-name-2    root    Wed Jan 11 15:23
> start guest-name-2    root    Wed Jan 11 16:28

Maybe it will be easier on admin's fingers to just call the above option --vm? I 
like shorter names if they make sense and are unambiguous.

 
> $ ./auvirt --full --uuid f937029b-93ca-4e13-b40b-663f46323503
> res   guest-name-2    root    Wed Jan 11 15:23    disk    "?"
> "/images/guest-2.img"
> res   guest-name-2    root    Wed Jan 11 15:23    vcpu    "0"    "4"
> res   guest-name-2    root    Wed Jan 11 15:23    net     "?"
> "52:54:00:DB:AE:B4"
> res   guest-name-2    root    Wed Jan 11 15:23    mem     "?"    "1048576"
> start guest-name-2    root    Wed Jan 11 15:23
> avc   guest-name-2    root    Wed Jan 11 19:49    read
> "/images/guest-2.img"    denied
> res   guest-name-2    root    Wed Jan 11 15:23    mem     "1048576"
> "2097152"
> stop  guest-name-2    root    Wed Jan 11 16:28

I would separate avcs and anomalies into a security report. Then for the 
resource section, I would rearrange the fields so the time is at the end and then 
show the duration so you collapse 2 lines (assignment and disposal) into 1 line.

For things that are disposed of at shutdown, you can just put "down" like last 
does when users are logged out by the system shutdown.

Overall, I think this is heading in the right direction.

Thanks,
 -Steve

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-13 19:23           ` Steve Grubb
@ 2012-01-13 19:45             ` Marcelo Cerri
  2012-01-13 20:56               ` Steve Grubb
  2012-01-16 13:05             ` Marcelo Cerri
  1 sibling, 1 reply; 21+ messages in thread
From: Marcelo Cerri @ 2012-01-13 19:45 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit, gcwilson, bryntcor

I'm also suppressing the AVC records. Maybe "--all-events" or 
"--show-all-events". What do you think?

On 01/13/2012 05:23 PM, Steve Grubb wrote:
> Hello,
>
> On Friday, January 13, 2012 12:25:05 PM Marcelo Cerri wrote:
>> These are some output examples of auvirt. What do you think?
> I think you are on the right track.
>
>
>> I just added a "--full" option because libvirt can generate several
>> resource events and this can make the output confusing.
> Hmm. Why not call it --resource if its a resource specific report? Full to me
> implies everything for all guests.
>
>
>> $ ./auvirt
>> start guest-name-1    root    Tue Jan 10 11:05
>> stop  guest-name-1    root    Tue Jan 10 11:39
>> start guest-name-2    root    Wed Jan 11 15:23
>> start guest-name-2    root    Wed Jan 11 16:28
>> start guest-name-1    root    Wed Jan 12 19:47
> Why not collapse these into 1 line like last that shows a duration?
>
> start guest-name-1    root    Tue Jan 10 11:05 - 11:39 (00:34)
>
> Do you have any samples for when a guest is paused and restarted? I would also
> collapse those into a line showing the duration of the pause.
>
> pause guest-name-1    root    Tue Jan 10 11:15 - 11:30 (00:15)
>
>
>> $ ./auvirt --show-uuid
>> start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan
>> 10 11:05
>> stop  guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan
>> 10 11:39
>> start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan
>> 11 15:23
>> start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan
>> 11 16:28
>> start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Wed Jan
>> 12 19:47
>>
>> $ ./auvirt --summary # keep the same behaviour
>>
>> $ ./auvirt --uuid fb4149f5-9ff6-4095-f6d3-a1d03936fdfa
>> start guest-name-1    root    Tue Jan 10 11:05
>> stop  guest-name-1    root    Tue Jan 10 11:39
>> start guest-name-1    root    Wed Jan 12 19:47
>>
>> $ ./auvirt --vm-name guest-name-2
>> start guest-name-2    root    Wed Jan 11 15:23
>> start guest-name-2    root    Wed Jan 11 16:28
> Maybe it will be easier on admin's fingers to just call the above option --vm? I
> like shorter names if they make sense and are unambiguous.
>
>
>> $ ./auvirt --full --uuid f937029b-93ca-4e13-b40b-663f46323503
>> res   guest-name-2    root    Wed Jan 11 15:23    disk    "?"
>> "/images/guest-2.img"
>> res   guest-name-2    root    Wed Jan 11 15:23    vcpu    "0"    "4"
>> res   guest-name-2    root    Wed Jan 11 15:23    net     "?"
>> "52:54:00:DB:AE:B4"
>> res   guest-name-2    root    Wed Jan 11 15:23    mem     "?"    "1048576"
>> start guest-name-2    root    Wed Jan 11 15:23
>> avc   guest-name-2    root    Wed Jan 11 19:49    read
>> "/images/guest-2.img"    denied
>> res   guest-name-2    root    Wed Jan 11 15:23    mem     "1048576"
>> "2097152"
>> stop  guest-name-2    root    Wed Jan 11 16:28
> I would separate avcs and anomalies into a security report. Then for the
> resource section, I would rearrange the fields so the time is at the end and then
> show the duration so you collapse 2 lines (assignment and disposal) into 1 line.
>
> For things that are disposed of at shutdown, you can just put "down" like last
> does when users are logged out by the system shutdown.
>
> Overall, I think this is heading in the right direction.
>
> Thanks,
>   -Steve
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-13 19:45             ` Marcelo Cerri
@ 2012-01-13 20:56               ` Steve Grubb
  0 siblings, 0 replies; 21+ messages in thread
From: Steve Grubb @ 2012-01-13 20:56 UTC (permalink / raw)
  To: Marcelo Cerri; +Cc: linux-audit, gcwilson, bryntcor

On Friday, January 13, 2012 02:45:14 PM Marcelo Cerri wrote:
> I'm also suppressing the AVC records. Maybe "--all-events" or

I like this one better                              ^^^^

-Steve

> "--show-all-events". What do you think?
>
> On 01/13/2012 05:23 PM, Steve Grubb wrote:
> > Hello,
> > 
> > On Friday, January 13, 2012 12:25:05 PM Marcelo Cerri wrote:
> >> These are some output examples of auvirt. What do you think?
> > 
> > I think you are on the right track.
> > 
> >> I just added a "--full" option because libvirt can generate several
> >> resource events and this can make the output confusing.
> > 
> > Hmm. Why not call it --resource if its a resource specific report? Full
> > to me implies everything for all guests.
> > 
> >> $ ./auvirt
> >> start guest-name-1    root    Tue Jan 10 11:05
> >> stop  guest-name-1    root    Tue Jan 10 11:39
> >> start guest-name-2    root    Wed Jan 11 15:23
> >> start guest-name-2    root    Wed Jan 11 16:28
> >> start guest-name-1    root    Wed Jan 12 19:47
> > 
> > Why not collapse these into 1 line like last that shows a duration?
> > 
> > start guest-name-1    root    Tue Jan 10 11:05 - 11:39 (00:34)
> > 
> > Do you have any samples for when a guest is paused and restarted? I would
> > also collapse those into a line showing the duration of the pause.
> > 
> > pause guest-name-1    root    Tue Jan 10 11:15 - 11:30 (00:15)
> > 
> >> $ ./auvirt --show-uuid
> >> start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan
> >> 10 11:05
> >> stop  guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan
> >> 10 11:39
> >> start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan
> >> 11 15:23
> >> start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan
> >> 11 16:28
> >> start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Wed Jan
> >> 12 19:47
> >> 
> >> $ ./auvirt --summary # keep the same behaviour
> >> 
> >> $ ./auvirt --uuid fb4149f5-9ff6-4095-f6d3-a1d03936fdfa
> >> start guest-name-1    root    Tue Jan 10 11:05
> >> stop  guest-name-1    root    Tue Jan 10 11:39
> >> start guest-name-1    root    Wed Jan 12 19:47
> >> 
> >> $ ./auvirt --vm-name guest-name-2
> >> start guest-name-2    root    Wed Jan 11 15:23
> >> start guest-name-2    root    Wed Jan 11 16:28
> > 
> > Maybe it will be easier on admin's fingers to just call the above option
> > --vm? I like shorter names if they make sense and are unambiguous.
> > 
> >> $ ./auvirt --full --uuid f937029b-93ca-4e13-b40b-663f46323503
> >> res   guest-name-2    root    Wed Jan 11 15:23    disk    "?"
> >> "/images/guest-2.img"
> >> res   guest-name-2    root    Wed Jan 11 15:23    vcpu    "0"    "4"
> >> res   guest-name-2    root    Wed Jan 11 15:23    net     "?"
> >> "52:54:00:DB:AE:B4"
> >> res   guest-name-2    root    Wed Jan 11 15:23    mem     "?"   
> >> "1048576" start guest-name-2    root    Wed Jan 11 15:23
> >> avc   guest-name-2    root    Wed Jan 11 19:49    read
> >> "/images/guest-2.img"    denied
> >> res   guest-name-2    root    Wed Jan 11 15:23    mem     "1048576"
> >> "2097152"
> >> stop  guest-name-2    root    Wed Jan 11 16:28
> > 
> > I would separate avcs and anomalies into a security report. Then for the
> > resource section, I would rearrange the fields so the time is at the end
> > and then show the duration so you collapse 2 lines (assignment and
> > disposal) into 1 line.
> > 
> > For things that are disposed of at shutdown, you can just put "down" like
> > last does when users are logged out by the system shutdown.
> > 
> > Overall, I think this is heading in the right direction.
> > 
> > Thanks,
> > 
> >   -Steve

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-13 19:23           ` Steve Grubb
  2012-01-13 19:45             ` Marcelo Cerri
@ 2012-01-16 13:05             ` Marcelo Cerri
  2012-01-16 15:36               ` Steve Grubb
  1 sibling, 1 reply; 21+ messages in thread
From: Marcelo Cerri @ 2012-01-16 13:05 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit, gcwilson, bryntcor

Hi,

Just some few questions:

What did yo mean by "a security report"? Just another section or a 
separated mode?

Wouldn't it be a problem to put the time field in the end of the 
resource records? It'd be like that:

res   guest-name-2   root  mem    "?"    "1048576"     Wed Jan 11 15:23 
- 15:24 (00:01)
start guest-name-2   root  Wed Jan 11 15:23 - 15:24 (00:01)

Or like that:

res   guest-name-2   root  mem    "?"    "1048576"     Wed Jan 11 15:23 
- 15:24 (00:01)
start guest-name-2   root                              Wed Jan 11 15:23 
- 15:24 (00:01)

Regards,
Marcelo

On 01/13/2012 05:23 PM, Steve Grubb wrote:
> Hello,
>
> On Friday, January 13, 2012 12:25:05 PM Marcelo Cerri wrote:
>> These are some output examples of auvirt. What do you think?
> I think you are on the right track.
>
>
>> I just added a "--full" option because libvirt can generate several
>> resource events and this can make the output confusing.
> Hmm. Why not call it --resource if its a resource specific report? Full to me
> implies everything for all guests.
>
>
>> $ ./auvirt
>> start guest-name-1    root    Tue Jan 10 11:05
>> stop  guest-name-1    root    Tue Jan 10 11:39
>> start guest-name-2    root    Wed Jan 11 15:23
>> start guest-name-2    root    Wed Jan 11 16:28
>> start guest-name-1    root    Wed Jan 12 19:47
> Why not collapse these into 1 line like last that shows a duration?
>
> start guest-name-1    root    Tue Jan 10 11:05 - 11:39 (00:34)
>
> Do you have any samples for when a guest is paused and restarted? I would also
> collapse those into a line showing the duration of the pause.
>
> pause guest-name-1    root    Tue Jan 10 11:15 - 11:30 (00:15)
>
>
>> $ ./auvirt --show-uuid
>> start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan
>> 10 11:05
>> stop  guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan
>> 10 11:39
>> start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan
>> 11 15:23
>> start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan
>> 11 16:28
>> start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Wed Jan
>> 12 19:47
>>
>> $ ./auvirt --summary # keep the same behaviour
>>
>> $ ./auvirt --uuid fb4149f5-9ff6-4095-f6d3-a1d03936fdfa
>> start guest-name-1    root    Tue Jan 10 11:05
>> stop  guest-name-1    root    Tue Jan 10 11:39
>> start guest-name-1    root    Wed Jan 12 19:47
>>
>> $ ./auvirt --vm-name guest-name-2
>> start guest-name-2    root    Wed Jan 11 15:23
>> start guest-name-2    root    Wed Jan 11 16:28
> Maybe it will be easier on admin's fingers to just call the above option --vm? I
> like shorter names if they make sense and are unambiguous.
>
>
>> $ ./auvirt --full --uuid f937029b-93ca-4e13-b40b-663f46323503
>> res   guest-name-2    root    Wed Jan 11 15:23    disk    "?"
>> "/images/guest-2.img"
>> res   guest-name-2    root    Wed Jan 11 15:23    vcpu    "0"    "4"
>> res   guest-name-2    root    Wed Jan 11 15:23    net     "?"
>> "52:54:00:DB:AE:B4"
>> res   guest-name-2    root    Wed Jan 11 15:23    mem     "?"    "1048576"
>> start guest-name-2    root    Wed Jan 11 15:23
>> avc   guest-name-2    root    Wed Jan 11 19:49    read
>> "/images/guest-2.img"    denied
>> res   guest-name-2    root    Wed Jan 11 15:23    mem     "1048576"
>> "2097152"
>> stop  guest-name-2    root    Wed Jan 11 16:28
> I would separate avcs and anomalies into a security report. Then for the
> resource section, I would rearrange the fields so the time is at the end and then
> show the duration so you collapse 2 lines (assignment and disposal) into 1 line.
>
> For things that are disposed of at shutdown, you can just put "down" like last
> does when users are logged out by the system shutdown.
>
> Overall, I think this is heading in the right direction.
>
> Thanks,
>   -Steve
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-16 13:05             ` Marcelo Cerri
@ 2012-01-16 15:36               ` Steve Grubb
  0 siblings, 0 replies; 21+ messages in thread
From: Steve Grubb @ 2012-01-16 15:36 UTC (permalink / raw)
  To: Marcelo Cerri; +Cc: linux-audit, gcwilson, bryntcor

On Monday, January 16, 2012 08:05:31 AM Marcelo Cerri wrote:
> Just some few questions:
> 
> What did yo mean by "a security report"? Just another section or a
> separated mode?

Probably a mode, because its data is not going to fit neatly into columns and 
look like a nice organized report.


> Wouldn't it be a problem to put the time field in the end of the
> resource records? It'd be like that:
> 
> res   guest-name-2   root  mem    "?"    "1048576"     Wed Jan 11 15:23
> - 15:24 (00:01)
> start guest-name-2   root  Wed Jan 11 15:23 - 15:24 (00:01)
> 
> Or like that:
> 
> res   guest-name-2   root  mem    "?"    "1048576"     Wed Jan 11 15:23
> - 15:24 (00:01)
> start guest-name-2   root                              Wed Jan 11 15:23
> - 15:24 (00:01)

Like the second one so that like fields line up verically. I'd try to make things 
line up vertically as much as possible even if there is blank space. (We might 
think of something later to add like perhaps the host machine name.)
 
-Steve


 
> On 01/13/2012 05:23 PM, Steve Grubb wrote:
> > Hello,
> > 
> > On Friday, January 13, 2012 12:25:05 PM Marcelo Cerri wrote:
> >> These are some output examples of auvirt. What do you think?
> > 
> > I think you are on the right track.
> > 
> >> I just added a "--full" option because libvirt can generate several
> >> resource events and this can make the output confusing.
> > 
> > Hmm. Why not call it --resource if its a resource specific report? Full
> > to me implies everything for all guests.
> > 
> >> $ ./auvirt
> >> start guest-name-1    root    Tue Jan 10 11:05
> >> stop  guest-name-1    root    Tue Jan 10 11:39
> >> start guest-name-2    root    Wed Jan 11 15:23
> >> start guest-name-2    root    Wed Jan 11 16:28
> >> start guest-name-1    root    Wed Jan 12 19:47
> > 
> > Why not collapse these into 1 line like last that shows a duration?
> > 
> > start guest-name-1    root    Tue Jan 10 11:05 - 11:39 (00:34)
> > 
> > Do you have any samples for when a guest is paused and restarted? I would
> > also collapse those into a line showing the duration of the pause.
> > 
> > pause guest-name-1    root    Tue Jan 10 11:15 - 11:30 (00:15)
> > 
> >> $ ./auvirt --show-uuid
> >> start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan
> >> 10 11:05
> >> stop  guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Tue Jan
> >> 10 11:39
> >> start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan
> >> 11 15:23
> >> start guest-name-2 f937029b-93ca-4e13-b40b-663f46323503 root    Wed Jan
> >> 11 16:28
> >> start guest-name-1 fb4149f5-9ff6-4095-f6d3-a1d03936fdfa root    Wed Jan
> >> 12 19:47
> >> 
> >> $ ./auvirt --summary # keep the same behaviour
> >> 
> >> $ ./auvirt --uuid fb4149f5-9ff6-4095-f6d3-a1d03936fdfa
> >> start guest-name-1    root    Tue Jan 10 11:05
> >> stop  guest-name-1    root    Tue Jan 10 11:39
> >> start guest-name-1    root    Wed Jan 12 19:47
> >> 
> >> $ ./auvirt --vm-name guest-name-2
> >> start guest-name-2    root    Wed Jan 11 15:23
> >> start guest-name-2    root    Wed Jan 11 16:28
> > 
> > Maybe it will be easier on admin's fingers to just call the above option
> > --vm? I like shorter names if they make sense and are unambiguous.
> > 
> >> $ ./auvirt --full --uuid f937029b-93ca-4e13-b40b-663f46323503
> >> res   guest-name-2    root    Wed Jan 11 15:23    disk    "?"
> >> "/images/guest-2.img"
> >> res   guest-name-2    root    Wed Jan 11 15:23    vcpu    "0"    "4"
> >> res   guest-name-2    root    Wed Jan 11 15:23    net     "?"
> >> "52:54:00:DB:AE:B4"
> >> res   guest-name-2    root    Wed Jan 11 15:23    mem     "?"   
> >> "1048576" start guest-name-2    root    Wed Jan 11 15:23
> >> avc   guest-name-2    root    Wed Jan 11 19:49    read
> >> "/images/guest-2.img"    denied
> >> res   guest-name-2    root    Wed Jan 11 15:23    mem     "1048576"
> >> "2097152"
> >> stop  guest-name-2    root    Wed Jan 11 16:28
> > 
> > I would separate avcs and anomalies into a security report. Then for the
> > resource section, I would rearrange the fields so the time is at the end
> > and then show the duration so you collapse 2 lines (assignment and
> > disposal) into 1 line.
> > 
> > For things that are disposed of at shutdown, you can just put "down" like
> > last does when users are logged out by the system shutdown.
> > 
> > Overall, I think this is heading in the right direction.
> > 
> > Thanks,
> > 
> >   -Steve

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-11 21:20     ` Steve Grubb
@ 2012-01-24 18:08       ` Marcelo Cerri
  2012-01-24 18:33         ` Marcelo Cerri
  2012-01-24 20:27         ` Steve Grubb
  0 siblings, 2 replies; 21+ messages in thread
From: Marcelo Cerri @ 2012-01-24 18:08 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit, gcwilson, bryntcor

I took a look at some anomaly events and I'm thinking to correlate them 
to guests based on the SELinux context or maybe based on the pid field.

Do you think there is another ways to correlate them?

Regards,
Marcelo

On 01/11/2012 07:20 PM, Steve Grubb wrote:
> On Thursday, January 05, 2012 11:44:57 AM Marcelo Cerri wrote:
>> But I'm not sure what means "anomaly events". Would it be malformed
>> records (without some fields, for example) or a specific record type
>> generated by the kernel or some other userspace application?
> No, these are events in the range of AUDIT_FIRST_ANOM_MSG and
> AUDIT_LAST_ANOM_MSG and some from the kernel in the range of
> AUDIT_FIRST_KERN_ANOM_MSG and AUDIT_LAST_KERN_ANOM_MSG.
>
> -Steve
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-24 18:08       ` Marcelo Cerri
@ 2012-01-24 18:33         ` Marcelo Cerri
  2012-01-24 20:27         ` Steve Grubb
  1 sibling, 0 replies; 21+ messages in thread
From: Marcelo Cerri @ 2012-01-24 18:33 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit, gcwilson, bryntcor

Maybe the session id?

On 01/24/2012 04:08 PM, Marcelo Cerri wrote:
> I took a look at some anomaly events and I'm thinking to correlate 
> them to guests based on the SELinux context or maybe based on the pid 
> field.
>
> Do you think there is another ways to correlate them?
>
> Regards,
> Marcelo
>
> On 01/11/2012 07:20 PM, Steve Grubb wrote:
>> On Thursday, January 05, 2012 11:44:57 AM Marcelo Cerri wrote:
>>> But I'm not sure what means "anomaly events". Would it be malformed
>>> records (without some fields, for example) or a specific record type
>>> generated by the kernel or some other userspace application?
>> No, these are events in the range of AUDIT_FIRST_ANOM_MSG and
>> AUDIT_LAST_ANOM_MSG and some from the kernel in the range of
>> AUDIT_FIRST_KERN_ANOM_MSG and AUDIT_LAST_KERN_ANOM_MSG.
>>
>> -Steve
>>
>
> -- 
> Linux-audit mailing list
> Linux-audit@redhat.com
> https://www.redhat.com/mailman/listinfo/linux-audit
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-24 18:08       ` Marcelo Cerri
  2012-01-24 18:33         ` Marcelo Cerri
@ 2012-01-24 20:27         ` Steve Grubb
  2012-01-25 12:56           ` Marcelo Cerri
  1 sibling, 1 reply; 21+ messages in thread
From: Steve Grubb @ 2012-01-24 20:27 UTC (permalink / raw)
  To: Marcelo Cerri; +Cc: linux-audit, gcwilson, bryntcor

On Tuesday, January 24, 2012 01:08:56 PM Marcelo Cerri wrote:
> I took a look at some anomaly events and I'm thinking to correlate them 
> to guests based on the SELinux context or maybe based on the pid field.
> 
> Do you think there is another ways to correlate them?

I was thinking to correlate them based on the time and pid. If its within the 
time range between startup/shutdown and its the same pid, then you have the 
event correlated. If its outside the time range or a different pid, then you do 
not have correlation. I would not look at selinux label because not all 
systems/distros have it enabled or compiled in. So, pid and time are the most 
universal identifiers for correlation.

-Steve

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-24 20:27         ` Steve Grubb
@ 2012-01-25 12:56           ` Marcelo Cerri
  2012-01-27 16:37             ` Marcelo Cerri
  0 siblings, 1 reply; 21+ messages in thread
From: Marcelo Cerri @ 2012-01-25 12:56 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit, gcwilson, bryntcor

I agree that pid and time is a better way for correlation but I was 
coding a solution based on that when I figured out a problem. There's no 
qemu pid in the audit logs. Libvirt (at least the libvirt shipped with 
RHEL 6.2) always logs its own pid to the audit log.

I'll try to discover if there is another way to correlate them or if 
newer versions of libvirt log the qemu pid to the audit log.

Regards,
Marcelo

On 01/24/2012 06:27 PM, Steve Grubb wrote:
> On Tuesday, January 24, 2012 01:08:56 PM Marcelo Cerri wrote:
>> I took a look at some anomaly events and I'm thinking to correlate them
>> to guests based on the SELinux context or maybe based on the pid field.
>>
>> Do you think there is another ways to correlate them?
> I was thinking to correlate them based on the time and pid. If its within the
> time range between startup/shutdown and its the same pid, then you have the
> event correlated. If its outside the time range or a different pid, then you do
> not have correlation. I would not look at selinux label because not all
> systems/distros have it enabled or compiled in. So, pid and time are the most
> universal identifiers for correlation.
>
> -Steve
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-25 12:56           ` Marcelo Cerri
@ 2012-01-27 16:37             ` Marcelo Cerri
  2012-01-27 17:21               ` Steve Grubb
  0 siblings, 1 reply; 21+ messages in thread
From: Marcelo Cerri @ 2012-01-27 16:37 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit, gcwilson, bryntcor

I submitted a patch to libvirt to add the qemu pid (vm-pid) to the 
VIRT_CONTROL audit record.

I'm using this field to correlate anomaly events to guest in auvirt and 
as a fallback it tries to use the SELinux context for that.

Regards,
Marcelo

On 01/25/2012 10:56 AM, Marcelo Cerri wrote:
> I agree that pid and time is a better way for correlation but I was 
> coding a solution based on that when I figured out a problem. There's 
> no qemu pid in the audit logs. Libvirt (at least the libvirt shipped 
> with RHEL 6.2) always logs its own pid to the audit log.
>
> I'll try to discover if there is another way to correlate them or if 
> newer versions of libvirt log the qemu pid to the audit log.
>
> Regards,
> Marcelo
>
> On 01/24/2012 06:27 PM, Steve Grubb wrote:
>> On Tuesday, January 24, 2012 01:08:56 PM Marcelo Cerri wrote:
>>> I took a look at some anomaly events and I'm thinking to correlate them
>>> to guests based on the SELinux context or maybe based on the pid field.
>>>
>>> Do you think there is another ways to correlate them?
>> I was thinking to correlate them based on the time and pid. If its 
>> within the
>> time range between startup/shutdown and its the same pid, then you 
>> have the
>> event correlated. If its outside the time range or a different pid, 
>> then you do
>> not have correlation. I would not look at selinux label because not all
>> systems/distros have it enabled or compiled in. So, pid and time are 
>> the most
>> universal identifiers for correlation.
>>
>> -Steve
>>
>
> -- 
> Linux-audit mailing list
> Linux-audit@redhat.com
> https://www.redhat.com/mailman/listinfo/linux-audit
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-27 16:37             ` Marcelo Cerri
@ 2012-01-27 17:21               ` Steve Grubb
  2012-01-27 17:31                 ` Marcelo Cerri
  0 siblings, 1 reply; 21+ messages in thread
From: Steve Grubb @ 2012-01-27 17:21 UTC (permalink / raw)
  To: Marcelo Cerri; +Cc: linux-audit, gcwilson, bryntcor

On Friday, January 27, 2012 11:37:00 AM Marcelo Cerri wrote:
> I submitted a patch to libvirt to add the qemu pid (vm-pid) to the 
> VIRT_CONTROL audit record.
>
> I'm using this field to correlate anomaly events to guest in auvirt and 
> as a fallback it tries to use the SELinux context for that.

OK, that sounds great. Its probably the best way to handle the situation. Do, 
you have a link to the patch or commit message?

Thanks,
-Steve

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-01-27 17:21               ` Steve Grubb
@ 2012-01-27 17:31                 ` Marcelo Cerri
  0 siblings, 0 replies; 21+ messages in thread
From: Marcelo Cerri @ 2012-01-27 17:31 UTC (permalink / raw)
  To: Steve Grubb; +Cc: linux-audit, gcwilson, bryntcor

Sure. Here it is.

http://libvirt.org/git/?p=libvirt.git;a=commit;h=98b01e8f2bf3dd3c8a8881f2a94af3f9d1a95620

Regards,
Marcelo

On 01/27/2012 03:21 PM, Steve Grubb wrote:
> On Friday, January 27, 2012 11:37:00 AM Marcelo Cerri wrote:
>> I submitted a patch to libvirt to add the qemu pid (vm-pid) to the
>> VIRT_CONTROL audit record.
>>
>> I'm using this field to correlate anomaly events to guest in auvirt and
>> as a fallback it tries to use the SELinux context for that.
> OK, that sounds great. Its probably the best way to handle the situation. Do,
> you have a link to the patch or commit message?
>
> Thanks,
> -Steve
>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH] auvirt: a new tool for reporting events related to virtual machines
  2012-02-01 17:16 Marcelo Cerri
@ 2012-02-03 18:52 ` Steve Grubb
  0 siblings, 0 replies; 21+ messages in thread
From: Steve Grubb @ 2012-02-03 18:52 UTC (permalink / raw)
  To: linux-audit; +Cc: gcwilson, bryntcor

On Wednesday, February 01, 2012 12:16:35 PM Marcelo Cerri wrote:
> This patch adds a new tool to extract information related to virtual
> machines from the audit log files.

Applied.

Thanks,
-Steve

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH] auvirt: a new tool for reporting events related to virtual machines
@ 2012-02-01 17:16 Marcelo Cerri
  2012-02-03 18:52 ` Steve Grubb
  0 siblings, 1 reply; 21+ messages in thread
From: Marcelo Cerri @ 2012-02-01 17:16 UTC (permalink / raw)
  To: linux-audit; +Cc: gcwilson, bryntcor

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@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@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

^ permalink raw reply related	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2012-02-03 18:52 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-15 15:56 [PATCH] auvirt: a new tool for reporting events related to virtual machines Marcelo Cerri
2011-12-20 18:18 ` Steve Grubb
2012-01-05 16:44   ` Marcelo Cerri
2012-01-09 17:00     ` Marcelo Cerri
2012-01-11 21:48       ` Steve Grubb
2012-01-13 17:25         ` Marcelo Cerri
2012-01-13 19:23           ` Steve Grubb
2012-01-13 19:45             ` Marcelo Cerri
2012-01-13 20:56               ` Steve Grubb
2012-01-16 13:05             ` Marcelo Cerri
2012-01-16 15:36               ` Steve Grubb
2012-01-11 21:20     ` Steve Grubb
2012-01-24 18:08       ` Marcelo Cerri
2012-01-24 18:33         ` Marcelo Cerri
2012-01-24 20:27         ` Steve Grubb
2012-01-25 12:56           ` Marcelo Cerri
2012-01-27 16:37             ` Marcelo Cerri
2012-01-27 17:21               ` Steve Grubb
2012-01-27 17:31                 ` Marcelo Cerri
2012-02-01 17:16 Marcelo Cerri
2012-02-03 18:52 ` Steve Grubb

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.