[PATCH - linux-next] ARM: ptrace: Fix audit caused compile error
by Peter Ujfalusi
While trying to compile the kernel for ARM (omap2plus_defconfig) the kernel
build fails with:
arch/arm/kernel/ptrace.c: In function ‘syscall_trace’:
arch/arm/kernel/ptrace.c:920:3: error: implicit declaration of function ‘audit_syscall_exit’
arch/arm/kernel/ptrace.c:922:3: error: implicit declaration of function ‘audit_syscall_entry’
arch/arm/kernel/ptrace.c:922:23: error: ‘AUDIT_ARCH_ARMEB’ undeclared (first use in this function)
arch/arm/kernel/ptrace.c:922:23: note: each undeclared identifier is reported only once for each function it appears in
make[1]: *** [arch/arm/kernel/ptrace.o] Error 1
The issue created by commit:
29ef73b7 Kernel: Audit Support For The ARM Platform
We need to include the linux/audit.h header to the arch/arm/kernel/ptrace.c
file to be able to compile the kernel.
Signed-off-by: Peter Ujfalusi <peter.ujfalusi(a)ti.com>
---
arch/arm/kernel/ptrace.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index e33870f..8e41ad2 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -15,6 +15,7 @@
#include <linux/elf.h>
#include <linux/smp.h>
#include <linux/ptrace.h>
+#include <linux/audit.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/init.h>
--
1.7.8.4
12 years, 8 months
[PATCH] auvirt: Remove workaround for VM name searching
by Marcelo Cerri
With this patch, the workaround for creating the search criteria is removed and
escaped fields are properly retrieved.
The unexpected single quote at the beginning of MAC addresses is fixed by a
patch in libvirt:
https://www.redhat.com/archives/libvir-list/2012-February/msg00502.html
---
tools/auvirt/auvirt.c | 39 +++++++++++++++------------------------
1 files changed, 15 insertions(+), 24 deletions(-)
diff --git a/tools/auvirt/auvirt.c b/tools/auvirt/auvirt.c
index c04780a..a89b097 100644
--- a/tools/auvirt/auvirt.c
+++ b/tools/auvirt/auvirt.c
@@ -312,23 +312,7 @@ int create_search_criteria(auparse_state_t *au)
}
}
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,
+ if (ausearch_add_interpreted_item(au, "vm", "=", vm,
AUSEARCH_RULE_AND)) {
fprintf(stderr, "Criteria error: id\n");
return 1;
@@ -390,7 +374,7 @@ int extract_virt_fields(auparse_state_t *au, const char **p_uuid,
if (p_name) {
if (!auparse_find_field(au, field = "vm"))
goto error;
- *p_name = auparse_get_field_str(au);
+ *p_name = auparse_interpret_field(au);
}
if (p_uuid) {
if (!auparse_find_field(au, field = "uuid"))
@@ -759,10 +743,11 @@ int process_resource_event(auparse_state_t *au)
strcmp("vcpu", res_type) == 0 ||
strcmp("mem", res_type) == 0 ||
strcmp("net", res_type) == 0) {
- const char *res;
+ const char *res = NULL;
/* Resource removed */
snprintf(field, sizeof(field), "old-%s", res_type);
- res = auparse_find_field(au, field);
+ if(auparse_find_field(au, field))
+ res = auparse_interpret_field(au);
if (res == NULL && debug) {
fprintf(stderr, "Failed to get %s field.\n", field);
} else {
@@ -771,8 +756,10 @@ int process_resource_event(auparse_state_t *au)
}
/* Resource added */
+ res = NULL;
snprintf(field, sizeof(field), "new-%s", res_type);
- res = auparse_find_field(au, field);
+ if (auparse_find_field(au, field))
+ res = auparse_interpret_field(au);
if (res == NULL && debug) {
fprintf(stderr, "Failed to get %s field.\n", field);
} else {
@@ -781,7 +768,9 @@ int process_resource_event(auparse_state_t *au)
}
} else if (strcmp("cgroup", res_type) == 0) {
auparse_first_record(au);
- const char *cgroup = auparse_find_field(au, "cgroup");
+ const char *cgroup = NULL;
+ if (auparse_find_field(au, "cgroup"))
+ cgroup = auparse_interpret_field(au);
rc += add_resource(au, uuid, uid, time, name, success, reason,
res_type, cgroup);
} else if (debug) {
@@ -856,8 +845,10 @@ int process_avc(auparse_state_t *au)
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"));
+ if (auparse_find_field(au, "comm"))
+ avc->comm = copy_str(auparse_interpret_field(au));
+ if (auparse_find_field(au, "name"))
+ avc->target = copy_str(auparse_interpret_field(au));
add_proof(avc, au);
if (list_append(events, avc) == NULL) {
event_free(avc);
--
1.7.1
12 years, 8 months
[PATCH] auvirt: Add security context to "relabel{to, from}" AVC records
by Marcelo Cerri
With this patch, auvirt prints one more field for AVC records when using the
"--all-events" option. The target context is added to "relabelto" AVC records
and the source context to "relabelfrom" AVC records.
---
tools/auvirt/auvirt.c | 17 ++++++++++++++++-
1 files changed, 16 insertions(+), 1 deletions(-)
diff --git a/tools/auvirt/auvirt.c b/tools/auvirt/auvirt.c
index e5c5ef6..9b71a3c 100644
--- a/tools/auvirt/auvirt.c
+++ b/tools/auvirt/auvirt.c
@@ -91,6 +91,7 @@ struct event {
char *comm;
char *seresult;
char *seperms;
+ char *context;
/* Fields to print proof information: */
struct record_id proof[4];
};
@@ -130,6 +131,7 @@ void event_free(struct event *event)
free(event->cgroup_class);
free(event->cgroup_detail);
free(event->cgroup_acl);
+ free(event->context);
free(event);
}
}
@@ -872,6 +874,18 @@ int process_avc(auparse_state_t *au)
avc->comm = copy_str(auparse_interpret_field(au));
if (auparse_find_field(au, "name"))
avc->target = copy_str(auparse_interpret_field(au));
+
+ /* get the context related to the permission that was denied. */
+ if (avc->seperms) {
+ const char *ctx = NULL;
+ if (strcmp("relabelfrom", avc->seperms) == 0) {
+ ctx = auparse_find_field(au, "scontext");
+ } else if (strcmp("relabelto", avc->seperms) == 0) {
+ ctx = auparse_find_field(au, "tcontext");
+ }
+ avc->context = copy_str(ctx);
+ }
+
add_proof(avc, au);
if (list_append(events, avc) == NULL) {
event_free(avc);
@@ -1140,7 +1154,8 @@ void print_event(struct event *event)
} 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("\t%s\t%s\t%s", N(event->comm), N(event->target),
+ N(event->context));
}
printf("\n");
--
1.7.1
12 years, 8 months
[PATCH] auvirt: Add details to cgroup records
by Marcelo Cerri
With this patch, auvirt prints additional information for cgroup records when
the option "--all-events" is given. It adds the device class, the path or
category of the device which is being allowed or denied, and the ACL.
---
tools/auvirt/auvirt.c | 31 ++++++++++++++++++++++++++++++-
1 files changed, 30 insertions(+), 1 deletions(-)
diff --git a/tools/auvirt/auvirt.c b/tools/auvirt/auvirt.c
index a89b097..e5c5ef6 100644
--- a/tools/auvirt/auvirt.c
+++ b/tools/auvirt/auvirt.c
@@ -80,6 +80,10 @@ struct event {
char *reason;
char *res_type;
char *res;
+ /* Fields specific for cgroup resources */
+ char *cgroup_class;
+ char *cgroup_detail;
+ char *cgroup_acl;
/* Fields specific for machine id events: */
char *seclevel;
/* Fields specific for avc events: */
@@ -123,6 +127,9 @@ void event_free(struct event *event)
free(event->comm);
free(event->seresult);
free(event->seperms);
+ free(event->cgroup_class);
+ free(event->cgroup_detail);
+ free(event->cgroup_acl);
free(event);
}
}
@@ -669,6 +676,22 @@ int add_resource(auparse_state_t *au, const char *uuid, uid_t uid, time_t time,
event->uid = uid;
event->start = time;
add_proof(event, au);
+
+ /* Get cgroup specific fields. */
+ if (strcmp("cgroup", res_type) == 0) {
+ event->cgroup_class = copy_str(auparse_find_field(au, "class"));
+ if (event->cgroup_class) {
+ const char *detail = NULL;
+ if (strcmp("path", event->cgroup_class) == 0) {
+ detail = auparse_find_field(au, "path");
+ } else if (strcmp("major", event->cgroup_class) == 0) {
+ detail = auparse_find_field(au, "category");
+ }
+ event->cgroup_detail = copy_str(detail);
+ }
+ event->cgroup_acl = copy_str(auparse_find_field(au, "acl"));
+ }
+
if (list_append(events, event) == NULL) {
event_free(event);
return 1;
@@ -1105,7 +1128,13 @@ void print_event(struct event *event)
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));
+ if (strcmp("cgroup", event->res_type) != 0) {
+ printf("\t%s", N(event->res));
+ } else {
+ printf("\t%s\t%s\t%s", N(event->cgroup_class),
+ N(event->cgroup_acl),
+ N(event->cgroup_detail));
+ }
} else if (event->type == ET_MACHINE_ID) {
printf("\t%s", N(event->seclevel));
} else if (event->type == ET_AVC) {
--
1.7.1
12 years, 8 months
AUTO: Gavin Appleton is out of the office. (returning 20/02/2012)
by Gavin Appleton
I am out of the office until 20/02/2012.
Note: This is an automated response to your message "Linux-audit Digest,
Vol 89, Issue 7" sent on 10/2/2012 5:00:07 PM.
This is the only notification you will receive while this person is away.
This e-mail is confidential and, if you are not the intended recipient, please return it to us and do not retain or disclose it. We filter and monitor e-mails in order to protect our system and the integrity, confidentiality and availability of e-mails. We cannot guarantee that e-mails are risk free and are not responsible for any related damage or unauthorised alteration of e-mails by third parties after sending.
For more information on Standard Life group, visit our website http://www.standardlife.com/
Standard Life plc (SC286832), Standard Life Assurance Limited* (SC286833) and Standard Life Employee Services Limited (SC271355) are all registered in Scotland at Standard Life House, 30 Lothian Road, Edinburgh EH1 2DH. *Authorised and regulated by the Financial Services Authority. 0131 225 2552. Calls may be recorded/monitored. Standard Life group includes Standard Life plc and its subsidiaries.
12 years, 8 months
[PATCH 1/2] auparse: Remove quotes from parsed fields
by Marcelo Cerri
Auparse just removes single quotes at the end of a field value and leaves
quotes at the beginning. With this patch, auparse removes quotes at the
beggining of a parsed field value and handles double quotes at the same way as
single quotes.
This is a simple test program to reproduce the problem:
-----
int main() {
const char *buffer= "type=VIRT_RESOURCE msg=audit(1327574186.046:174): user pid=6748 uid=0 auid=500 ses=1 subj=unconfined_u:unconfined_r:unconfined_t:s0 msg='virt=kvm resrc=net reason=start vm=\"CentOS\" uuid=fb4149f5-9ff6-4095-f6d3-a1d03936fdfa old-net='?' new-net='52:54:00:DB:AE:B4 test': exe=\"/usr/sbin/libvirtd\" hostname=? addr=? terminal=? res=success'\n";
auparse_state_t *au = auparse_init(AUSOURCE_BUFFER, buffer);
if (au == NULL) return -1;
while (auparse_next_event(au) > 0) {
printf("%s\n", auparse_find_field(au, "new-net"));
}
auparse_destroy(au);
return 0;
}
-----
---
auparse/ellist.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/auparse/ellist.c b/auparse/ellist.c
index eafcfee..8c3061d 100644
--- a/auparse/ellist.c
+++ b/auparse/ellist.c
@@ -137,6 +137,9 @@ static int parse_up_record(rnode* r)
// Remove beginning cruft of name
if (*ptr == '(')
ptr++;
+ // Remove quotes
+ if (*val == '\'' || *val == '"')
+ val++;
n.name = strdup(ptr);
n.val = strdup(val);
// Remove trailing punctuation
@@ -149,7 +152,8 @@ static int parse_up_record(rnode* r)
n.val[len-1] = 0;
len--;
}
- if (len && n.val[len-1] == '\'') {
+ if (len && (n.val[len - 1] == '\''
+ || n.val[len - 1] == '"')) {
n.val[len-1] = 0;
len--;
}
--
1.7.1
12 years, 8 months
audit.rules
by Jender, Raymond [USA]
How would you set up audit.rules to log any action by administrators?
Thanks,
Ray
12 years, 8 months
[PATCH] ausearch: Fix parsing of uid in user space events
by Marcelo Cerri
The function parse_user aborts when it tries to parse a record which contains
an uuid field (usually a VIRT_* record).
---
src/ausearch-parse.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/ausearch-parse.c b/src/ausearch-parse.c
index d38ed56..f83f84b 100644
--- a/src/ausearch-parse.c
+++ b/src/ausearch-parse.c
@@ -719,7 +719,7 @@ static int parse_user(const lnode *n, search_items *s)
str = strstr(term, "auid=");
if (str == NULL) { // Try the older one
str = strstr(term, "loginuid=");
- if (str == NULL)
+ if (str == NULL)
return 7;
ptr = str + 9;
} else
@@ -792,7 +792,7 @@ static int parse_user(const lnode *n, search_items *s)
term = str;
while (*term != ' ' && *term != ':')
term++;
- if (term == str)
+ if (term == str)
return 24;
saved = *term;
*term = 0;
@@ -804,9 +804,9 @@ static int parse_user(const lnode *n, search_items *s)
// USER_LOGIN for example.
str = strstr(term, "uid=");
if (str) {
- if (*(str-1)=='a' || *(str-1)=='s')
+ if (*(str - 1) == 'a' || *(str - 1) == 's' || *(str - 1) == 'u')
goto skip;
- if (!(*(str-1)=='\'' || *(str-1)==' '))
+ if (!(*(str - 1) == '\'' || *(str - 1) == ' '))
return 25;
ptr = str + 4;
term = ptr;
--
1.7.1
12 years, 8 months
Question about Memory leaks in 1.7
by larry.erdahl@usbank.com
I was wonder if the memory leaks that were addressed in 1.8 were in all
releases of 1.7, or if there were specific releases of 1.7 that 1.8
addressed. The reason being is that I have a mixed Linux environment and
I'm working with Novell on what looks like a memory leak. Novell hasn't
yet moved to 2.0 and isn't yet ready to adopt 1.8. They're asking me to
test their other supported release of 1.7.7, which I really don't want to
do if there are known leaks in these releases.
Any clarification or help would be appreciated
Thanks...
Larry E. Erdahl
Information Security Services
Computer Security Incident Response Team (CSIRT)
1 Meridian Crossing
Richfield, MN 55423
Mail Code: EP-MN-MS6I
Office Phone: (612)973-7153
U.S. BANCORP made the following annotations
---------------------------------------------------------------------
Electronic Privacy Notice. This e-mail, and any attachments, contains information that is, or may be, covered by electronic communications privacy laws, and is also confidential and proprietary in nature. If you are not the intended recipient, please be advised that you are legally prohibited from retaining, using, copying, distributing, or otherwise disclosing this information in any manner. Instead, please reply to the sender that you have received this communication in error, and then immediately delete it. Thank you in advance for your cooperation.
---------------------------------------------------------------------
12 years, 9 months
[PATCH] auvirt: a new tool for reporting events related to virtual machines
by Marcelo Cerri
This patch adds a new tool to extract information related to virtual machines
from the audit log files.
This tool is based on the proposal sent in the RFC:
https://www.redhat.com/archives/linux-audit/2011-November/msg00014.html
---
audit.spec | 2 +
configure.ac | 2 +-
tools/Makefile.am | 2 +-
tools/auvirt/Makefile.am | 40 ++
tools/auvirt/auvirt-list.c | 105 ++++
tools/auvirt/auvirt-list.h | 31 +
tools/auvirt/auvirt.8 | 121 ++++
tools/auvirt/auvirt.c | 1308 ++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 1609 insertions(+), 2 deletions(-)
create mode 100644 tools/auvirt/Makefile.am
create mode 100644 tools/auvirt/auvirt-list.c
create mode 100644 tools/auvirt/auvirt-list.h
create mode 100644 tools/auvirt/auvirt.8
create mode 100644 tools/auvirt/auvirt.c
diff --git a/audit.spec b/audit.spec
index 383bed1..a9940b4 100644
--- a/audit.spec
+++ b/audit.spec
@@ -172,6 +172,7 @@ fi
%attr(644,root,root) %{_mandir}/man8/autrace.8.gz
%attr(644,root,root) %{_mandir}/man8/aulast.8.gz
%attr(644,root,root) %{_mandir}/man8/aulastlog.8.gz
+%attr(644,root,root) %{_mandir}/man8/auvirt.8.gz
%attr(644,root,root) %{_mandir}/man8/ausyscall.8.gz
%attr(644,root,root) %{_mandir}/man7/audit.rules.7.gz
%attr(644,root,root) %{_mandir}/man5/auditd.conf.5.gz
@@ -186,6 +187,7 @@ fi
%attr(755,root,root) %{_bindir}/aulast
%attr(755,root,root) %{_bindir}/aulastlog
%attr(755,root,root) %{_bindir}/ausyscall
+%attr(755,root,root) %{_bindir}/auvirt
%attr(755,root,root) /etc/rc.d/init.d/auditd
%attr(750,root,root) %dir %{_var}/log/audit
%attr(750,root,root) %dir /etc/audit
diff --git a/configure.ac b/configure.ac
index c1cf96f..b7f7fcd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -255,7 +255,7 @@ AC_SUBST(libev_LIBS)
AC_SUBST(LIBPRELUDE_CFLAGS)
AC_SUBST(LIBPRELUDE_LDFLAGS)
-AC_OUTPUT(Makefile lib/Makefile lib/test/Makefile auparse/Makefile auparse/test/Makefile src/Makefile src/mt/Makefile src/libev/Makefile src/test/Makefile swig/Makefile docs/Makefile init.d/Makefile audisp/Makefile audisp/plugins/Makefile audisp/plugins/builtins/Makefile audisp/plugins/prelude/Makefile audisp/plugins/remote/Makefile audisp/plugins/zos-remote/Makefile bindings/Makefile bindings/python/Makefile tools/Makefile tools/aulast/Makefile tools/aulastlog/Makefile tools/ausyscall/Makefile)
+AC_OUTPUT(Makefile lib/Makefile lib/test/Makefile auparse/Makefile auparse/test/Makefile src/Makefile src/mt/Makefile src/libev/Makefile src/test/Makefile swig/Makefile docs/Makefile init.d/Makefile audisp/Makefile audisp/plugins/Makefile audisp/plugins/builtins/Makefile audisp/plugins/prelude/Makefile audisp/plugins/remote/Makefile audisp/plugins/zos-remote/Makefile bindings/Makefile bindings/python/Makefile tools/Makefile tools/aulast/Makefile tools/aulastlog/Makefile tools/ausyscall/Makefile tools/auvirt/Makefile)
echo .
echo "
diff --git a/tools/Makefile.am b/tools/Makefile.am
index 3b7acfe..15ba254 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -22,5 +22,5 @@
CONFIG_CLEAN_FILES = *.loT *.rej *.orig
-SUBDIRS = aulast aulastlog ausyscall
+SUBDIRS = aulast aulastlog ausyscall auvirt
diff --git a/tools/auvirt/Makefile.am b/tools/auvirt/Makefile.am
new file mode 100644
index 0000000..0e9ec5b
--- /dev/null
+++ b/tools/auvirt/Makefile.am
@@ -0,0 +1,40 @@
+#
+# Makefile.am --
+# Copyright (c) 2011 IBM Corp.
+# All Rights Reserved.
+#
+# This software may be freely redistributed and/or modified under the
+# terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 2, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; see the file COPYING. If not, write to the
+# Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# Authors:
+# Marcelo Henrique Cerri <mhcerri(a)br.ibm.com>
+#
+
+CONFIG_CLEAN_FILES = *.loT *.rej *.orig
+AUTOMAKE_OPTIONS = no-dependencies
+EXTRA_DIST = $(man_MANS)
+INCLUDES = -I${top_srcdir} \
+ -I${top_srcdir}/lib \
+ -I${top_srcdir}/auparse \
+ -I${top_srcdir}/src
+LIBS = -L${top_builddir}/auparse \
+ -lauparse
+AM_CFLAGS = -D_GNU_SOURCE
+bin_PROGRAMS = auvirt
+noinst_HEADERS = auvirt-list.h
+man_MANS = auvirt.8
+
+auvirt_SOURCES = auvirt.c \
+ auvirt-list.c \
+ ${top_srcdir}/src/ausearch-time.c
diff --git a/tools/auvirt/auvirt-list.c b/tools/auvirt/auvirt-list.c
new file mode 100644
index 0000000..7502188
--- /dev/null
+++ b/tools/auvirt/auvirt-list.c
@@ -0,0 +1,105 @@
+#include "auvirt-list.h"
+#include <stdlib.h>
+
+list_t *list_init(list_t *list, list_free_data_fn *free_data_fn)
+{
+ if (list == NULL)
+ return NULL;
+ list->head = list->tail = NULL;
+ list->free_data_fn = free_data_fn;
+ return list;
+}
+
+list_t *list_new(list_free_data_fn *free_data_fn)
+{
+ return list_init(malloc(sizeof(list_t)), free_data_fn);
+}
+
+void list_free_node(list_node_t *node, list_free_data_fn *free_data_fn)
+{
+ if (node) {
+ if (free_data_fn)
+ free_data_fn(node->data);
+ free(node);
+ }
+}
+
+void list_free_(list_t *list, list_free_data_fn *free_data_fn)
+{
+ if (list != NULL) {
+ list_node_t *it = list->head;
+ while (it && it->next) {
+ it = it->next;
+ list_free_node(it->prev, free_data_fn);
+ }
+ list_free_node(it, free_data_fn);
+ free(list);
+ }
+}
+
+void list_free(list_t *list)
+{
+ if (list)
+ list_free_(list, list->free_data_fn);
+}
+
+list_node_t *list_insert_after(list_t *list, list_node_t *it,
+ void *data)
+{
+ list_node_t *node = NULL;
+ if (list == NULL)
+ return NULL;
+
+ /* allocate node */
+ node = malloc(sizeof(list_node_t));
+ if (node == NULL)
+ return NULL;
+ node->data = data;
+
+ /* insert the new node after it */
+ node->prev = it;
+ if (it) {
+ node->next = it->next;
+ it->next = node;
+ }
+ else
+ node->next = list->head;
+ if (node->next)
+ node->next->prev = node;
+
+ /* update list's head and tail */
+ if (it == list->tail)
+ list->tail = node;
+ if (it == NULL)
+ list->head = node;
+
+ return node;
+}
+
+list_node_t *list_append(list_t *list, void *data)
+{
+ return list_insert_after(list, list->tail, data);
+}
+
+int list_remove_(list_t *list, list_node_t *it,
+ list_free_data_fn *free_data_fn)
+{
+ if (list == NULL || it == NULL)
+ return 1;
+ if (list->head == it)
+ list->head = it->next;
+ if (list->tail == it)
+ list->tail = it->prev;
+ if (it->next)
+ it->next->prev = it->prev;
+ if (it->prev)
+ it->prev->next = it->next;
+ list_free_node(it, free_data_fn);
+ return 0;
+}
+
+int list_remove(list_t *list, list_node_t *it)
+{
+ return list_remove_(list, it, list->free_data_fn);
+}
+
diff --git a/tools/auvirt/auvirt-list.h b/tools/auvirt/auvirt-list.h
new file mode 100644
index 0000000..fb58746
--- /dev/null
+++ b/tools/auvirt/auvirt-list.h
@@ -0,0 +1,31 @@
+
+#ifndef AUVIRT_LIST_HEADER
+#define AUVIRT_LIST_HEADER
+
+typedef void (list_free_data_fn)(void *data);
+
+typedef struct list_node_t {
+ void *data;
+ struct list_node_t *prev;
+ struct list_node_t *next;
+} list_node_t;
+
+typedef struct list_t {
+ list_node_t *head;
+ list_node_t *tail;
+ list_free_data_fn *free_data_fn;
+} list_t;
+
+list_t *list_init(list_t *list, list_free_data_fn *free_data_fn);
+list_t *list_new(list_free_data_fn *free_data_fn);
+void list_free_(list_t *list, list_free_data_fn *free_data_fn);
+void list_free(list_t *list);
+list_node_t *list_insert_after(list_t *list, list_node_t *it,
+ void *data);
+list_node_t *list_append(list_t *list, void *data);
+int list_remove_(list_t *list, list_node_t *it,
+ list_free_data_fn *free_data_fn);
+int list_remove(list_t *list, list_node_t *it);
+
+#endif
+
diff --git a/tools/auvirt/auvirt.8 b/tools/auvirt/auvirt.8
new file mode 100644
index 0000000..86266f7
--- /dev/null
+++ b/tools/auvirt/auvirt.8
@@ -0,0 +1,121 @@
+.TH AUVIRT 8 "Dec 2011" "IBM Corp" "System Administration Utilities"
+.SH NAME
+auvirt - a program that shows data related to virtual machines
+
+.SH SYNOPSIS
+.B auvirt
+[ \fIOPTIONS\fP ]
+
+.SH DESCRIPTION
+\fBauvirt\fP shows a list of guest sessions found in the audit logs. If a guest
+is specified, only the events related to that guest is considered. To specify a
+guest, both UUID or VM name can be given.
+
+For each guest session the tool prints a record with the domain name, the user
+that started the guest, the time when the guest was started and the time when
+the guest was stoped.
+
+If the option "--all-events" is given a more detailed output is shown. In this
+mode other records are shown for guest's stops, resource
+assignments, host shutdowns and AVC and anomaly events. The first field
+indicates the event type and can have the following values: start, stop,
+res, avc, anom and down (for host shutdowns).
+
+Resource assignments have the additional fields: resource type, reason and
+resource. And AVC records have the following additional fields: operation,
+result, command and target.
+
+By default, auvirt reads records from the system audit log file. But
+\fB--stdin\fP and \fB--file\fP options can be specified to change this
+behavior.
+
+.SH OPTIONS
+.TP
+\fB--all-events\fP
+Show records for all virtualization related events.
+.TP
+\fB--debug\fP
+Print debug messages to standard output.
+.TP
+\fB-f\fP, \fB--file\fP \fIfile\fP
+Read records from the given \fIfile\fP instead from the system audit log file.
+.TP
+\fB-h\fP, \fB--help\fP
+Print help message and exit.
+.TP
+\fB--proof\fP
+Add after each event a line containing all the identifiers of the audit records
+used to calculate the event. Each identifier consists of unix time,
+milliseconds and serial number.
+.TP
+\fB--show-uuid\fP
+Add the guest's UUID to each record.
+.TP
+\fB--stdin\fP
+Read records from the standard input instead from the system audit log file.
+This option cannot be specified with \fB--file\fP.
+.TP
+\fB--summary\fP
+Print a summary with information about the events found. The summary contains
+the considered range of time, the number of guest starts and stops, the number
+of resource assignments, the number of AVC and anomaly events, the number of
+host shutdowns and the number of failed operations.
+.TP
+.BR \-te ,\ \-\-end \ [\fIend-date\fP]\ [\fIend-time\fP]
+Search for events with time stamps equal to or before the given end time. The
+format of end time depends on your locale. If the date is omitted,
+.B today
+is assumed. If the time is omitted,
+.B now
+is assumed. Use 24 hour clock time rather than AM or PM to specify time.
+An example date using the en_US.utf8 locale is 09/03/2009. An example of time
+is 18:00:00. The date format accepted is influenced by the LC_TIME
+environmental variable.
+
+You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP,
+\fByesterday\fP, \fBthis\-week\fP, \fBweek\-ago\fP, \fBthis\-month\fP,
+\fBthis\-year\fP. \fBToday\fP means starting now. \fBRecent\fP is 10 minutes
+ago. \fBYesterday\fP is 1 second after midnight the previous day.
+\fBThis\-week\fP means starting 1 second after midnight on day 0 of the week
+determined by your locale (see \fBlocaltime\fP). \fBThis\-month\fP means 1
+second after midnight on day 1 of the month. \fBThis\-year\fP means the 1
+second after midnight on the first day of the first month.
+.TP
+.BR \-ts ,\ \-\-start \ [\fIstart-date\fP]\ [\fIstart-time\fP]
+Search for events with time stamps equal to or after the given end time. The
+format of end time depends on your locale. If the date is omitted,
+.B today
+is assumed. If the time is omitted,
+.B midnight
+is assumed. Use 24 hour clock time rather than AM or PM to specify time. An
+example date using the en_US.utf8 locale is 09/03/2009. An example of time is
+18:00:00. The date format accepted is influenced by the LC_TIME environmental
+variable.
+
+You may also use the word: \fBnow\fP, \fBrecent\fP, \fBtoday\fP,
+\fByesterday\fP, \fBthis\-week\fP, \fBthis\-month\fP, \fBthis\-year\fP.
+\fBToday\fP means starting at 1 second after midnight. \fBRecent\fP is 10
+minutes ago. \fBYesterday\fP is 1 second after midnight the previous day.
+\fBThis\-week\fP means starting 1 second after midnight on day 0 of the week
+determined by your locale (see \fBlocaltime\fP). \fBThis\-month\fP means 1
+second after midnight on day 1 of the month. \fBThis\-year\fP means the 1
+second after midnight on the first day of the first month.
+.TP
+\fB-u\fP, \fB--uuid\fP \ \fIUUID\fP
+Only show events related to the guest with the given UUID.
+.TP
+\fB-v\fP, \fB--vm\fP \ \fIname\fP
+Only show events related to the guest with the given name.
+
+.SH EXAMPLES
+To see all the records in this month for a guest
+
+\fBauvirt --start this-month --vm GuestVmName --all-events\fP
+
+.SH SEE ALSO
+.BR aulast (8),
+.BR ausearch (8),
+.BR aureport (8).
+
+.SH AUTHOR
+Marcelo Cerri
diff --git a/tools/auvirt/auvirt.c b/tools/auvirt/auvirt.c
new file mode 100644
index 0000000..c04780a
--- /dev/null
+++ b/tools/auvirt/auvirt.c
@@ -0,0 +1,1308 @@
+/*
+ * auvirt.c - A tool to extract data related to virtualization.
+ * Copyright (c) 2011 IBM Corp.
+ * All Rights Reserved.
+ *
+ * This software may be freely redistributed and/or modified under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors:
+ * Marcelo Henrique Cerri <mhcerri(a)br.ibm.com>
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <locale.h>
+#include <string.h>
+#include <regex.h>
+#include <time.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include "auparse.h"
+#include "libaudit.h"
+#include "ausearch-time.h"
+#include "auvirt-list.h"
+
+/* Command line parameters */
+static int help_flag = 0;
+static int stdin_flag = 0;
+static int summary_flag = 0;
+static int all_events_flag = 0;
+static int uuid_flag = 0;
+static int proof_flag = 0;
+static const char *vm = NULL;
+static const char *uuid = NULL;
+static const char *file = NULL;
+static int debug = 0;
+/*
+ * The start time and end time given in the command line is stored respectively
+ * in the variables start_time and end_time that are declared/defined in the
+ * files ausearch-time.h and ausearch-time.c. These files are reused from the
+ * ausearch tool source code:
+ *
+ * time_t start_time = 0;
+ * time_t end_time = 0;
+ */
+
+/* List of events */
+enum event_type {
+ ET_NONE = 0, ET_START, ET_STOP, ET_MACHINE_ID, ET_AVC, ET_RES, ET_ANOM,
+ ET_DOWN
+};
+struct record_id {
+ time_t time;
+ unsigned int milli;
+ unsigned long serial;
+};
+struct event {
+ enum event_type type;
+ time_t start;
+ time_t end;
+ uid_t uid;
+ char *uuid;
+ char *name;
+ int success;
+ pid_t pid;
+ /* Fields specific for resource events: */
+ char *reason;
+ char *res_type;
+ char *res;
+ /* Fields specific for machine id events: */
+ char *seclevel;
+ /* Fields specific for avc events: */
+ char *target;
+ char *comm;
+ char *seresult;
+ char *seperms;
+ /* Fields to print proof information: */
+ struct record_id proof[4];
+};
+list_t *events = NULL;
+
+
+/* Auxiliary functions to allocate and to free events. */
+struct event *event_alloc()
+{
+ struct event *event = malloc(sizeof(struct event));
+ if (event) {
+ /* The new event is initialized with values that represents
+ * unset values: -1 for uid and pid and 0 (or NULL) for numbers
+ * and pointers. For example, event->end = 0 represents an
+ * unfinished event.
+ */
+ memset(event, 0, sizeof(struct event));
+ event->uid = -1;
+ event->pid = -1;
+ }
+ return event;
+}
+
+void event_free(struct event *event)
+{
+ if (event) {
+ free(event->uuid);
+ free(event->name);
+ free(event->reason);
+ free(event->res_type);
+ free(event->res);
+ free(event->seclevel);
+ free(event->target);
+ free(event->comm);
+ free(event->seresult);
+ free(event->seperms);
+ free(event);
+ }
+}
+
+inline char *copy_str(const char *str)
+{
+ return (str) ? strdup(str) : NULL;
+}
+
+void usage(FILE *output)
+{
+ fprintf(output, "usage: auvirt [--stdin] [--all-events] [--summary] "
+ "[--start start-date [start-time]] "
+ "[--end end-date [end-time]] [--file file-name] "
+ "[--show--uuid] [--proof] "
+ "[--uuid uuid] [--vm vm-name]\n");
+}
+
+/* Parse and check command line arguments */
+int parse_args(int argc, char **argv)
+{
+ /* Based on http://www.ietf.org/rfc/rfc4122.txt */
+ const char *uuid_pattern = "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-"
+ "[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$";
+ int i, rc = 0;
+ regex_t uuid_regex;
+
+ if (regcomp(&uuid_regex, uuid_pattern, REG_EXTENDED)) {
+ fprintf(stderr, "Failed to initialize program.\n");
+ return 1;
+ }
+
+ for (i = 1; i < argc; i++) {
+ const char *opt = argv[i];
+ if (opt[0] != '-') {
+ fprintf(stderr, "Argument not expected: %s\n", opt);
+ goto error;
+ } else if (strcmp("--vm", opt) == 0 ||
+ strcmp("-v", opt) == 0) {
+ if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+ fprintf(stderr, "\"%s\" option requires "
+ "an argument.\n", opt);
+ goto error;
+ }
+ vm = argv[++i];
+ } else if (strcmp("--uuid", opt) == 0 ||
+ strcmp("-u", opt) == 0) {
+ if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+ fprintf(stderr, "\"%s\" option requires "
+ "an argument.\n", opt);
+ goto error;
+ }
+ if (regexec(&uuid_regex, argv[i + 1], 0, NULL, 0)) {
+ fprintf(stderr, "Invalid uuid: %s\n",
+ argv[i + 1]);
+ goto error;
+ }
+ uuid = argv[++i];
+ } else if (strcmp("--all-events", opt) == 0 ||
+ strcmp("-a", opt) == 0) {
+ all_events_flag = 1;
+ } else if (strcmp("--summary", opt) == 0 ||
+ strcmp("-s", opt) == 0) {
+ summary_flag = 1;
+ } else if (strcmp("--file", opt) == 0 ||
+ strcmp("-f", opt) == 0) {
+ if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+ fprintf(stderr, "\"%s\" option requires "
+ "an argument.\n", opt);
+ goto error;
+ }
+ file = argv[++i];
+ } else if (strcmp("--show-uuid", opt) == 0) {
+ uuid_flag = 1;
+ } else if (strcmp("--stdin", opt) == 0) {
+ stdin_flag = 1;
+ } else if (strcmp("--proof", opt) == 0) {
+ proof_flag = 1;
+ } else if (strcmp("--help", opt) == 0 ||
+ strcmp("-h", opt) == 0) {
+ help_flag = 1;
+ goto exit;
+ } else if (strcmp("--start", opt) == 0 ||
+ strcmp("-ts", opt) == 0) {
+ const char *date, *time = NULL;
+ if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+ fprintf(stderr, "\"%s\" option requires at "
+ "least one argument.\n", opt);
+ goto error;
+ }
+ date = argv[++i];
+ if ((i + 1) < argc && argv[i + 1][0] != '-')
+ time = argv[++i];
+ /* This will set start_time */
+ if(ausearch_time_start(date, time))
+ goto error;
+ } else if (strcmp("--end", opt) == 0 ||
+ strcmp("-te", opt) == 0) {
+ const char *date, *time = NULL;
+ if ((i + 1) >= argc || argv[i + 1][0] == '-') {
+ fprintf(stderr, "\"%s\" option requires at "
+ "least one argument.\n", opt);
+ goto error;
+ }
+ date = argv[++i];
+ if ((i + 1) < argc && argv[i + 1][0] != '-')
+ time = argv[++i];
+ /* This will set end_time */
+ if (ausearch_time_end(date, time))
+ goto error;
+ } else if (strcmp("--debug", opt) == 0) {
+ debug = 1;
+ } else {
+ fprintf(stderr, "Unknown option \"%s\".\n", opt);
+ goto error;
+ }
+ }
+
+ /* Validate conflicting options */
+ if (stdin_flag && file) {
+ fprintf(stderr, "\"--sdtin\" and \"--file\" options "
+ "must not be specified together.\n");
+ goto error;
+ }
+
+ if (debug) {
+ fprintf(stderr, "help_flag='%i'\n", help_flag);
+ fprintf(stderr, "stdin_flag='%i'\n", stdin_flag);
+ fprintf(stderr, "all_events_flag='%i'\n", all_events_flag);
+ fprintf(stderr, "summary_flag='%i'\n", summary_flag);
+ fprintf(stderr, "uuid='%s'\n", uuid ? uuid : "(null)");
+ fprintf(stderr, "vm='%s'\n", vm ? vm : "(null)");
+ fprintf(stderr, "file='%s'\n", file ? file : "(null)");
+ fprintf(stderr, "start_time='%-.16s'\n", (start_time == 0L) ?
+ "" : ctime(&start_time));
+ fprintf(stderr, "end_time='%-.16s'\n", (end_time == 0L) ?
+ "" : ctime(&end_time));
+ }
+
+exit:
+ regfree(&uuid_regex);
+ return rc;
+error:
+ rc = 1;
+ goto exit;
+}
+
+/* Initialize an auparse_state_t with the correct log source. */
+auparse_state_t *init_auparse()
+{
+ auparse_state_t *au = NULL;
+ if (stdin_flag) {
+ au = auparse_init(AUSOURCE_FILE_POINTER, stdin);
+ } else if (file) {
+ au = auparse_init(AUSOURCE_FILE, file);
+ } else {
+ if (getuid()) {
+ fprintf(stderr, "You probably need to be root for "
+ "this to work\n");
+ }
+ au = auparse_init(AUSOURCE_LOGS, NULL);
+ }
+ if (au == NULL) {
+ fprintf(stderr, "Error: %s\n", strerror(errno));
+ }
+ return au;
+}
+
+/* Create a criteria to search for the virtualization related records */
+int create_search_criteria(auparse_state_t *au)
+{
+ char *error = NULL;
+ char expr[1024];
+ snprintf(expr, sizeof(expr),
+ "(\\record_type >= %d && \\record_type <= %d)",
+ AUDIT_FIRST_VIRT_MSG, AUDIT_LAST_VIRT_MSG);
+ if (ausearch_add_expression(au, expr, &error, AUSEARCH_RULE_CLEAR)) {
+ fprintf(stderr, "Criteria error: %s\n", error);
+ free(error);
+ return 1;
+ }
+ if (uuid) {
+ if (ausearch_add_item(au, "uuid", "=", uuid,
+ AUSEARCH_RULE_AND)) {
+ fprintf(stderr, "Criteria error: uuid\n");
+ return 1;
+ }
+ }
+ if (vm) {
+ /*
+ * If a field has its value quoted in the audit log, for
+ * example:
+ * vm="guest-name"
+ *
+ * auparse will consider the field value with quotes when
+ * matching a rule. For example, using the example above the
+ * following rule will not match:
+ * ausearch_add_item(au, "vm", "=", "guest-name", how);
+ *
+ * But this rule will match:
+ * ausearch_add_item(au, "vm", "=", "\"guest-name\"", how);
+ *
+ * TODO use a better approach for this problem...
+ */
+ snprintf(expr, sizeof(expr), "\"%s\"", vm);
+ if (ausearch_add_item(au, "vm", "=", expr,
+ AUSEARCH_RULE_AND)) {
+ fprintf(stderr, "Criteria error: id\n");
+ return 1;
+ }
+ }
+ if (all_events_flag || summary_flag) {
+ if (ausearch_add_item(au, "type", "=", "AVC",
+ AUSEARCH_RULE_OR)) {
+ fprintf(stderr, "Criteria error: AVC\n");
+ return 1;
+ }
+ if (ausearch_add_item(au, "type", "=", "SYSTEM_SHUTDOWN",
+ AUSEARCH_RULE_OR)) {
+ fprintf(stderr, "Criteria error: shutdown\n");
+ return 1;
+ }
+ snprintf(expr, sizeof(expr),
+ "(\\record_type >= %d && \\record_type <= %d) ||"
+ "(\\record_type >= %d && \\record_type <= %d)",
+ AUDIT_FIRST_ANOM_MSG, AUDIT_LAST_ANOM_MSG,
+ AUDIT_FIRST_KERN_ANOM_MSG, AUDIT_LAST_KERN_ANOM_MSG);
+ if (ausearch_add_expression(au, expr, &error,
+ AUSEARCH_RULE_OR)) {
+ fprintf(stderr, "Criteria error: %s\n", error);
+ free(error);
+ return 1;
+ }
+ }
+ if (start_time) {
+ if (ausearch_add_timestamp_item(au, ">=", start_time, 0,
+ AUSEARCH_RULE_AND)) {
+ fprintf(stderr, "Criteria error: start_time\n");
+ return 1;
+ }
+ }
+ if (end_time) {
+ if (ausearch_add_timestamp_item(au, "<=", end_time, 0,
+ AUSEARCH_RULE_AND)) {
+ fprintf(stderr, "Criteria error: end_time\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Extract the most common fields from virtualization-related records. */
+int extract_virt_fields(auparse_state_t *au, const char **p_uuid,
+ uid_t *p_uid, time_t *p_time, const char **p_name,
+ int *p_suc)
+{
+ const char *field;
+ auparse_first_record(au);
+ /* Order matters */
+ if (p_uid) {
+ if (!auparse_find_field(au, field = "uid"))
+ goto error;
+ *p_uid = auparse_get_field_int(au);
+ }
+ if (p_name) {
+ if (!auparse_find_field(au, field = "vm"))
+ goto error;
+ *p_name = auparse_get_field_str(au);
+ }
+ if (p_uuid) {
+ if (!auparse_find_field(au, field = "uuid"))
+ goto error;
+ *p_uuid = auparse_get_field_str(au);
+ }
+ if (p_suc) {
+ const char *res = auparse_find_field(au, field = "res");
+ if (res == NULL)
+ goto error;
+ *p_suc = (strcmp("success", res) == 0) ? 1 : 0;
+ }
+ if (p_time) {
+ *p_time = auparse_get_time(au);
+ }
+ return 0;
+
+error:
+ if (debug) {
+ fprintf(stderr, "Failed to get field \"%s\" for record "
+ "%ld.%03u:%lu\n", field ? field : "",
+ auparse_get_time(au),
+ auparse_get_milli(au),
+ auparse_get_serial(au));
+ }
+ return 1;
+}
+
+/* Return label and categories from a security context. */
+const char *get_seclevel(const char *seclabel)
+{
+ /*
+ * system_u:system_r:svirt_t:s0:c107,c434
+ * \____ _____/
+ * '
+ * level + cat
+ */
+ int c = 0;
+ for (;seclabel && *seclabel; seclabel++) {
+ if (*seclabel == ':')
+ c += 1;
+ if (c == 3)
+ return seclabel + 1;
+ }
+ return NULL;
+}
+
+int add_proof(struct event *event, auparse_state_t *au)
+{
+ if (!proof_flag)
+ return 0;
+
+ size_t i, proof_len = sizeof(event->proof)/sizeof(event->proof[0]);
+ for (i = 0; i < proof_len; i++) {
+ if (event->proof[i].time == 0)
+ break;
+ }
+ if (i == proof_len) {
+ if (debug)
+ fprintf(stderr, "Failed to add proof.\n");
+ return 1;
+ }
+
+ event->proof[i].time = auparse_get_time(au);
+ event->proof[i].milli = auparse_get_milli(au);
+ event->proof[i].serial = auparse_get_serial(au);
+ return 0;
+}
+
+/*
+ * machine_id records are used to get the selinux context associated to a
+ * guest.
+ */
+int process_machine_id_event(auparse_state_t *au)
+{
+ uid_t uid;
+ time_t time;
+ const char *seclevel, *uuid, *name;
+ struct event *event;
+ int success;
+
+ seclevel = get_seclevel(auparse_find_field(au, "vm-ctx"));
+ if (seclevel == NULL) {
+ if (debug)
+ fprintf(stderr, "security context not found for "
+ "MACHINE_ID event.\n");
+ }
+
+ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+ return 0;
+
+ event = event_alloc();
+ if (event == NULL)
+ return 1;
+ event->type = ET_MACHINE_ID;
+ event->uuid = copy_str(uuid);
+ event->name = copy_str(name);
+ event->success = success;
+ event->seclevel = copy_str(seclevel);
+ event->uid = uid;
+ event->start = time;
+ add_proof(event, au);
+ if (list_append(events, event) == NULL) {
+ event_free(event);
+ return 1;
+ }
+ return 0;
+}
+
+int add_start_guest_event(auparse_state_t *au)
+{
+ struct event *start;
+ uid_t uid;
+ time_t time;
+ const char *uuid, *name;
+ int success;
+ list_node_t *it;
+
+ /* Just skip this record if it failed to get some of the fields */
+ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+ return 0;
+
+ /* On failure, loop backwards to update all the resources associated to
+ * the last session of this guest. When a machine_id or a stop event is
+ * found the loop can be broken because a machine_id is created at the
+ * beginning of a session and a stop event indicates a previous
+ * session.
+ */
+ if (!success) {
+ for (it = events->tail; it; it = it->prev) {
+ struct event *event = it->data;
+ if (event->success && event->uuid &&
+ strcmp(uuid, event->uuid) == 0) {
+ if (event->type == ET_STOP ||
+ event->type == ET_MACHINE_ID) {
+ /* An old session found. */
+ break;
+ } else if (event->type == ET_RES &&
+ event->end == 0) {
+ event->end = time;
+ add_proof(event, au);
+ }
+ }
+ }
+ }
+
+ start = event_alloc();
+ if (start == NULL)
+ return 1;
+ start->type = ET_START;
+ start->uuid = copy_str(uuid);
+ start->name = copy_str(name);
+ start->success = success;
+ start->uid = uid;
+ start->start = time;
+ auparse_first_record(au);
+ if (auparse_find_field(au, "vm-pid"))
+ start->pid = auparse_get_field_int(au);
+ add_proof(start, au);
+ if (list_append(events, start) == NULL) {
+ event_free(start);
+ return 1;
+ }
+ return 0;
+}
+
+int add_stop_guest_event(auparse_state_t *au)
+{
+ list_node_t *it;
+ struct event *stop, *start = NULL, *event = NULL;
+ uid_t uid;
+ time_t time;
+ const char *uuid, *name;
+ int success;
+
+ /* Just skip this record if it failed to get some of the fields */
+ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+ return 0;
+
+ /* Loop backwards to find the last start event for the uuid and
+ * update all resource records related to that guest session.
+ */
+ for (it = events->tail; it; it = it->prev) {
+ event = it->data;
+ if (event->success && event->uuid &&
+ strcmp(uuid, event->uuid) == 0) {
+ if (event->type == ET_START) {
+ /* If an old session is found it's no longer
+ * necessary to update the resource records.
+ */
+ if (event->end || start)
+ break;
+ /* This is the start event related to the
+ * current session. */
+ start = event;
+ } else if (event->type == ET_STOP ||
+ event->type == ET_MACHINE_ID) {
+ /* Old session found. */
+ break;
+ } else if (event->type == ET_RES && event->end == 0) {
+ /* Update the resource assignments. */
+ event->end = time;
+ add_proof(event, au);
+ }
+ }
+ }
+ if (start == NULL) {
+ if (debug) {
+ fprintf(stderr, "Couldn't find the correlated start i"
+ "record to the stop event.\n");
+ }
+ return 0;
+ }
+
+ /* Create a new stop event */
+ stop = event_alloc();
+ if (stop == NULL)
+ return 1;
+ stop->type = ET_STOP;
+ stop->uuid = copy_str(uuid);
+ stop->name = copy_str(name);
+ stop->success = success;
+ stop->uid = uid;
+ stop->start = time;
+ auparse_first_record(au);
+ if (auparse_find_field(au, "vm-pid"))
+ stop->pid = auparse_get_field_int(au);
+ add_proof(stop, au);
+ if (list_append(events, stop) == NULL) {
+ event_free(stop);
+ return 1;
+ }
+
+ /* Update the correlated start event. */
+ if (success) {
+ start->end = time;
+ add_proof(start, au);
+ }
+ return 0;
+}
+
+int process_control_event(auparse_state_t *au)
+{
+ const char *op;
+
+ op = auparse_find_field(au, "op");
+ if (op == NULL) {
+ if (debug)
+ fprintf(stderr, "Invalid op field.\n");
+ return 0;
+ }
+
+ if (strcmp("start", op) == 0) {
+ if (add_start_guest_event(au))
+ return 1;
+ } else if (strcmp("stop", op) == 0) {
+ if (add_stop_guest_event(au))
+ return 1;
+ } else if (debug) {
+ fprintf(stderr, "Unknown op: %s\n", op);
+ }
+ return 0;
+}
+
+inline int is_resource(const char *res)
+{
+ if (res == NULL ||
+ res[0] == '\0' ||
+ strcmp("0", res) == 0 ||
+ strcmp("?", res) == 0)
+ return 0;
+ return 1;
+}
+
+int add_resource(auparse_state_t *au, const char *uuid, uid_t uid, time_t time,
+ const char *name, int success, const char *reason,
+ const char *res_type, const char *res)
+{
+ if (!is_resource(res))
+ return 0;
+
+ struct event *event = event_alloc();
+ if (event == NULL)
+ return 1;
+ event->type = ET_RES;
+ event->uuid = copy_str(uuid);
+ event->name = copy_str(name);
+ event->success = success;
+ event->reason = copy_str(reason);
+ event->res_type = copy_str(res_type);
+ event->res = copy_str(res);
+ event->uid = uid;
+ event->start = time;
+ add_proof(event, au);
+ if (list_append(events, event) == NULL) {
+ event_free(event);
+ return 1;
+ }
+ return 0;
+}
+
+int update_resource(auparse_state_t *au, const char *uuid, uid_t uid,
+ time_t time, const char *name, int success, const char *reason,
+ const char *res_type, const char *res)
+{
+ if (!is_resource(res) || !success)
+ return 0;
+
+ list_node_t *it;
+ struct event *start = NULL;
+
+ /* Find the last start event for the uuid */
+ for (it = events->tail; it; it = it->prev) {
+ start = it->data;
+ if (start->type == ET_RES &&
+ start->success &&
+ start->uuid &&
+ strcmp(uuid, start->uuid) == 0 &&
+ strcmp(res_type, start->res_type) == 0 &&
+ strcmp(res, start->res) == 0)
+ break;
+ }
+ if (it == NULL) {
+ if (debug) {
+ fprintf(stderr, "Couldn't find the correlated resource"
+ " record to update.\n");
+ }
+ return 0;
+ }
+
+ start->end = time;
+ add_proof(start, au);
+ return 0;
+}
+
+int process_resource_event(auparse_state_t *au)
+{
+ uid_t uid;
+ time_t time;
+ const char *res_type, *uuid, *name;
+ char field[64];
+ const char *reason;
+ int success;
+
+ /* Just skip this record if it failed to get some of the fields */
+ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success))
+ return 0;
+
+ /* Get the resource type */
+ auparse_first_record(au);
+ res_type = auparse_find_field(au, "resrc");
+ reason = auparse_find_field(au, "reason");
+ if (res_type == NULL) {
+ if (debug)
+ fprintf(stderr, "Invalid resrc field.\n");
+ return 0;
+ }
+
+ /* Resource records with these types have old and new values. New
+ * values indicate resources assignments and are added to the event
+ * list. Old values are used to update the end time of a resource
+ * assignment.
+ */
+ int rc = 0;
+ if (strcmp("disk", res_type) == 0 ||
+ strcmp("vcpu", res_type) == 0 ||
+ strcmp("mem", res_type) == 0 ||
+ strcmp("net", res_type) == 0) {
+ const char *res;
+ /* Resource removed */
+ snprintf(field, sizeof(field), "old-%s", res_type);
+ res = auparse_find_field(au, field);
+ if (res == NULL && debug) {
+ fprintf(stderr, "Failed to get %s field.\n", field);
+ } else {
+ rc += update_resource(au, uuid, uid, time, name,
+ success, reason, res_type, res);
+ }
+
+ /* Resource added */
+ snprintf(field, sizeof(field), "new-%s", res_type);
+ res = auparse_find_field(au, field);
+ if (res == NULL && debug) {
+ fprintf(stderr, "Failed to get %s field.\n", field);
+ } else {
+ rc += add_resource(au, uuid, uid, time, name, success,
+ reason, res_type, res);
+ }
+ } else if (strcmp("cgroup", res_type) == 0) {
+ auparse_first_record(au);
+ const char *cgroup = auparse_find_field(au, "cgroup");
+ rc += add_resource(au, uuid, uid, time, name, success, reason,
+ res_type, cgroup);
+ } else if (debug) {
+ fprintf(stderr, "Found an unknown resource: %s.\n",
+ res_type);
+ }
+ return rc;
+}
+
+/* Search for the last machine_id record with the given seclevel */
+struct event *get_machine_id_by_seclevel(const char *seclevel)
+{
+ struct event *machine_id = NULL;
+ list_node_t *it;
+
+ for (it = events->tail; it; it = it->prev) {
+ struct event *event = it->data;
+ if (event->type == ET_MACHINE_ID &&
+ event->seclevel != NULL &&
+ strcmp(event->seclevel, seclevel) == 0) {
+ machine_id = event;
+ break;
+ }
+ }
+
+ return machine_id;
+}
+
+/* AVC records are correlated to guest through the selinux context. */
+int process_avc(auparse_state_t *au)
+{
+ const char *target, *seclevel;
+ struct event *machine_id, *avc;
+ uid_t uid;
+ time_t time;
+
+ seclevel = get_seclevel(auparse_find_field(au, "tcontext"));
+ if (seclevel == NULL) {
+ if (debug) {
+ fprintf(stderr, "Security context not found for "
+ "AVC event.\n");
+ }
+ return 0;
+ }
+
+ if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
+ return 0;
+
+ machine_id = get_machine_id_by_seclevel(seclevel);
+ if (machine_id == NULL) {
+ if (debug) {
+ fprintf(stderr, "Couldn't get the security level from "
+ "the AVC event.\n");
+ }
+ return 0;
+ }
+
+ avc = event_alloc();
+ if (avc == NULL)
+ return 1;
+ avc->type = ET_AVC;
+
+ /* Guest info */
+ avc->uuid = copy_str(machine_id->uuid);
+ avc->name = copy_str(machine_id->name);
+ memcpy(avc->proof, machine_id->proof, sizeof(avc->proof));
+
+ /* AVC info */
+ avc->start = time;
+ avc->uid = uid;
+ avc->seclevel = copy_str(seclevel);
+ auparse_first_record(au);
+ avc->seresult = copy_str(auparse_find_field(au, "seresult"));
+ avc->seperms = copy_str(auparse_find_field(au, "seperms"));
+ avc->comm = copy_str(auparse_find_field(au, "comm"));
+ avc->target = copy_str(auparse_find_field(au, "name"));
+ add_proof(avc, au);
+ if (list_append(events, avc) == NULL) {
+ event_free(avc);
+ return 1;
+ }
+ return 0;
+}
+
+/* This function tries to correlate an anomaly record to a guest using the qemu
+ * pid or the selinux context. */
+int process_anom(auparse_state_t *au)
+{
+ uid_t uid;
+ time_t time;
+ pid_t pid = -1;
+ list_node_t *it;
+ struct event *anom, *start = NULL;
+
+ /* An anomaly record is correlated to a guest by the process id */
+ if (auparse_find_field(au, "pid")) {
+ pid = auparse_get_field_int(au);
+ } else {
+ if (debug) {
+ fprintf(stderr, "Found an anomaly record "
+ "without pid.\n");
+ }
+ }
+
+ /* Loop backwards to find a running guest with the same pid. */
+ if (pid >= 0) {
+ for (it = events->tail; it; it = it->next) {
+ struct event *event = it->data;
+ if (event->pid == pid && event->success) {
+ if (event->type == ET_STOP) {
+ break;
+ } else if (event->type == ET_START) {
+ if (event->end == 0)
+ start = event;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Try to match using selinux context */
+ if (start == NULL) {
+ const char *seclevel;
+ struct event *machine_id;
+
+ seclevel = get_seclevel(auparse_find_field(au, "subj"));
+ if (seclevel == NULL) {
+ if (debug) {
+ auparse_first_record(au);
+ const char *text = auparse_get_record_text(au);
+ fprintf(stderr, "Security context not found "
+ "for anomaly event: %s\n",
+ text ? text : "");
+ }
+ return 0;
+ }
+ machine_id = get_machine_id_by_seclevel(seclevel);
+ if (machine_id == NULL) {
+ if (debug) {
+ fprintf(stderr, "Couldn't get the security "
+ "level from the anomaly event.\n");
+ }
+ return 0;
+ }
+
+ for (it = events->tail; it; it = it->next) {
+ struct event *event = it->data;
+ if (event->success &&
+ strcmp(machine_id->uuid, event->uuid) == 0) {
+ if (event->type == ET_STOP) {
+ break;
+ } else if (event->type == ET_START) {
+ if (event->end == 0)
+ start = event;
+ break;
+ }
+ }
+ }
+ }
+
+ if (start == NULL) {
+ if (debug) {
+ const char *text = auparse_get_record_text(au);
+ fprintf(stderr, "Guest not found for "
+ "anomaly record: %s.\n",
+ text ? text : "");
+ }
+ return 0;
+ }
+
+ if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
+ return 0;
+
+ anom = event_alloc();
+ if (anom == NULL)
+ return 1;
+ anom->type = ET_ANOM;
+ anom->uuid = copy_str(start->uuid);
+ anom->name = copy_str(start->name);
+ anom->uid = uid;
+ anom->start = time;
+ anom->pid = pid;
+ memcpy(anom->proof, start->proof, sizeof(anom->proof));
+ add_proof(anom, au);
+ if (list_append(events, anom) == NULL) {
+ event_free(anom);
+ return 1;
+ }
+ return 0;
+}
+
+int process_shutdown(auparse_state_t *au)
+{
+ uid_t uid = -1;
+ time_t time = 0;
+ struct event *down;
+ list_node_t *it;
+ int success = 0;
+
+ if (extract_virt_fields(au, NULL, &uid, &time, NULL, &success))
+ return 0;
+
+ for (it = events->tail; it; it = it->prev) {
+ struct event *event = it->data;
+ if (event->success) {
+ if (event->type == ET_START || event->type == ET_RES) {
+ if (event->end == 0) {
+ event->end = time;
+ add_proof(event, au);
+ }
+ } else if (event->type == ET_DOWN) {
+ break;
+ }
+ }
+ }
+
+ down = event_alloc();
+ if (down == NULL)
+ return 1;
+ down->type = ET_DOWN;
+ down->uid = uid;
+ down->start = time;
+ down->success = success;
+ add_proof(down, au);
+ if (list_append(events, down) == NULL) {
+ event_free(down);
+ return 1;
+ }
+ return 0;
+}
+
+/* Convert record type to a string */
+const char *get_rec_type(struct event *e)
+{
+ static char buf[64];
+ if (e == NULL)
+ return "";
+
+ switch (e->type) {
+ case ET_START:
+ return "start";
+ case ET_STOP:
+ return "stop";
+ case ET_RES:
+ return "res";
+ case ET_AVC:
+ return "avc";
+ case ET_ANOM:
+ return "anom";
+ case ET_DOWN:
+ return "down";
+ }
+
+ snprintf(buf, sizeof(buf), "%d", e->type);
+ return buf;
+}
+
+/* Convert uid to a string */
+const char *get_username(struct event *e)
+{
+ static char s[256];
+ if (!e || e->uid < 0) {
+ s[0] = '?';
+ s[1] = '\0';
+ } else {
+ struct passwd *passwd = getpwuid(e->uid);
+ if (passwd == NULL || passwd->pw_name == NULL) {
+ snprintf(s, sizeof(s), "%d", e->uid);
+ } else {
+ snprintf(s, sizeof(s), "%s", passwd->pw_name);
+ }
+ }
+ return s;
+}
+
+/* Convert a time period to string */
+const char *get_time_period(struct event *event)
+{
+ size_t i = 0;
+ static char buf[128];
+
+ i += sprintf(buf + i, "%-16.16s", ctime(&event->start));
+ if (event->end) {
+ time_t secs = event->end - event->start;
+ int mins, hours, days;
+ i += sprintf(buf + i, " - %-7.5s", ctime(&event->end) + 11);
+ mins = (secs / 60) % 60;
+ hours = (secs / 3600) % 24;
+ days = secs / 86400;
+ if (days) {
+ i += sprintf(buf + i, "(%d+%02d:%02d)", days, hours,
+ mins);
+ } else {
+ i += sprintf(buf + i, "(%02d:%02d)", hours, mins);
+ }
+ } else {
+ if (!event->success &&
+ event->type != ET_AVC &&
+ event->type != ET_ANOM) {
+ i += sprintf(buf + i, " - failed");
+ }
+ }
+ return buf;
+}
+
+void print_event(struct event *event)
+{
+ /* Auxiliary macro to convert NULL to "" */
+ #define N(str) ((str) ? str : "")
+
+ /* machine id records are used just to get information about
+ * the guests. */
+ if (event->type == ET_MACHINE_ID)
+ return;
+ /* If "--all-events" is not given, only the start event is shown. */
+ if (!all_events_flag && event->type != ET_START)
+ return;
+ /* The type of event is shown only when all records are shown */
+ if (all_events_flag)
+ printf("%-5.5s ", get_rec_type(event));
+
+ /* Print common fields */
+ printf("%-25.25s", N(event->name));
+ if (uuid_flag)
+ printf("\t%-36.36s", N(event->uuid));
+ printf("\t%-11.11s\t%-35.35s", get_username(event),
+ get_time_period(event));
+
+ /* Print type specific fields */
+ if (event->type == ET_RES) {
+ printf("\t%-12.12s", N(event->res_type));
+ printf("\t%-10.10s", N(event->reason));
+ printf("\t%s", N(event->res));
+ } else if (event->type == ET_MACHINE_ID) {
+ printf("\t%s", N(event->seclevel));
+ } else if (event->type == ET_AVC) {
+ printf("\t%-12.12s", N(event->seperms));
+ printf("\t%-10.10s", N(event->seresult));
+ printf("\t%s\t%s", N(event->comm), N(event->target));
+ }
+ printf("\n");
+
+ /* Print proof */
+ if (proof_flag) {
+ int first = 1;
+ int i, len = sizeof(event->proof)/sizeof(event->proof[0]);
+ printf(" Proof:");
+ for (i = 0; i < len; i++) {
+ if (event->proof[i].time) {
+ printf("%s %ld.%03u:%lu",
+ (first) ? "" : ",",
+ event->proof[i].time,
+ event->proof[i].milli,
+ event->proof[i].serial);
+ first = 0;
+ }
+ }
+ printf("\n\n");
+ }
+}
+
+/* Print all events */
+void print_events()
+{
+ list_node_t *it;
+ for (it = events->head; it; it = it->next) {
+ struct event *event = it->data;
+ if (event)
+ print_event(event);
+ }
+}
+
+/* Count and print summary */
+void print_summary()
+{
+ /* Summary numbers */
+ time_t start_time = 0, end_time = 0;
+ long start = 0, stop = 0, res = 0, avc = 0, anom = 0,
+ shutdown = 0, failure = 0;
+
+ /* Calculate summary */
+ list_node_t *it;
+ for (it = events->head; it; it = it->next) {
+ struct event *event = it->data;
+ if (event->success == 0 &&
+ (event->type == ET_START ||
+ event->type == ET_STOP ||
+ event->type == ET_RES)) {
+ failure++;
+ } else {
+ switch (event->type) {
+ case ET_START:
+ start++;
+ break;
+ case ET_STOP:
+ stop++;
+ break;
+ case ET_RES:
+ res++;
+ break;
+ case ET_AVC:
+ avc++;
+ break;
+ case ET_ANOM:
+ anom++;
+ break;
+ case ET_DOWN:
+ shutdown++;
+ break;
+ }
+ }
+
+ /* Calculate time range */
+ if (event->start) {
+ if (start_time == 0 || event->start < start_time) {
+ start_time = event->start;
+ }
+ if (end_time == 0 || event->start > end_time) {
+ end_time = event->start;
+ }
+ }
+ if (event->end) {
+ if (start_time == 0 || event->end < start_time) {
+ start_time = event->end;
+ }
+ if (end_time == 0 || event->end > end_time) {
+ end_time = event->end;
+ }
+ }
+
+ }
+
+ /* Print summary */
+ printf("Range of time for report: %-.16s - %-.16s\n",
+ (start_time) ? ctime(&start_time) : "undef",
+ (end_time) ? ctime(&end_time) : "undef");
+ printf("Number of guest starts: %ld\n", start);
+ printf("Number of guest stops: %ld\n", stop);
+ printf("Number of resource assignments: %ld\n", res);
+ printf("Number of related AVCs: %ld\n", avc);
+ printf("Number of related anomalies: %ld\n", anom);
+ printf("Number of host shutdowns: %ld\n", shutdown);
+ printf("Number of failed operations: %ld\n", failure);
+}
+
+int main(int argc, char **argv)
+{
+ int rc = 0;
+ auparse_state_t *au = NULL;
+
+ setlocale(LC_ALL, "");
+ if (parse_args(argc, argv))
+ goto error;
+ if (help_flag) {
+ usage(stdout);
+ goto exit;
+ }
+
+ /* Initialize event list*/
+ events = list_new((list_free_data_fn*) event_free);
+ if (events == NULL)
+ goto unexpected_error;
+
+ /* Initialize auparse */
+ au = init_auparse();
+ if (au == NULL)
+ goto error;
+ if (create_search_criteria(au))
+ goto error;
+
+ while (ausearch_next_event(au) > 0) {
+ const char *op;
+ int err = 0;
+
+ switch(auparse_get_type(au)) {
+ case AUDIT_VIRT_MACHINE_ID:
+ err = process_machine_id_event(au);
+ break;
+ case AUDIT_VIRT_CONTROL:
+ err = process_control_event(au);
+ break;
+ case AUDIT_VIRT_RESOURCE:
+ err = process_resource_event(au);
+ break;
+ case AUDIT_AVC:
+ err = process_avc(au);
+ break;
+ case AUDIT_FIRST_ANOM_MSG ... AUDIT_LAST_ANOM_MSG:
+ case AUDIT_FIRST_KERN_ANOM_MSG ... AUDIT_LAST_KERN_ANOM_MSG:
+ err = process_anom(au);
+ break;
+ case AUDIT_SYSTEM_SHUTDOWN:
+ err = process_shutdown(au);
+ break;
+ }
+ if (err) {
+ goto unexpected_error;
+ }
+ auparse_next_event(au);
+ }
+
+ /* Show results */
+ if (summary_flag) {
+ print_summary();
+ } else {
+ print_events();
+ }
+
+ /* success */
+ goto exit;
+
+unexpected_error:
+ fprintf(stderr, "Unexpected error\n");
+error:
+ rc = 1;
+exit:
+ if (au)
+ auparse_destroy(au);
+ list_free(events);
+ if (debug)
+ fprintf(stdout, "Exit code: %d\n", rc);
+ return rc;
+}
+
--
1.7.1
12 years, 9 months