public inbox for linux-audit@redhat.com
 help / color / mirror / Atom feed
From: Marcelo Cerri <mhcerri@linux.vnet.ibm.com>
To: linux-audit@redhat.com
Cc: gcwilson@us.ibm.com, bryntcor@us.ibm.com
Subject: [PATCH 2/2] auvirt: Add support for AVC records generated by AppArmor
Date: Mon, 20 Feb 2012 16:15:47 -0200	[thread overview]
Message-ID: <1329761747-27905-3-git-send-email-mhcerri@linux.vnet.ibm.com> (raw)
In-Reply-To: <1329761747-27905-1-git-send-email-mhcerri@linux.vnet.ibm.com>

This patch adds support for matching AVC records generated by AppArmor. With
this patch auvirt matches AVC records based on AppArmor profile name generated
by libvirt, which contains the guest's UUID, and based on target name ("name"
field), which auvirt tries to correlate to resources assigned to the guests.
---
 tools/auvirt/auvirt.c |  226 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 225 insertions(+), 1 deletions(-)

diff --git a/tools/auvirt/auvirt.c b/tools/auvirt/auvirt.c
index a49a8b8..7c0e769 100644
--- a/tools/auvirt/auvirt.c
+++ b/tools/auvirt/auvirt.c
@@ -894,7 +894,7 @@ int process_avc_selinux_context(auparse_state_t *au, const char *context)
 }
 
 /* AVC records are correlated to guest through the selinux context. */
-int process_avc(auparse_state_t *au)
+int process_avc_selinux(auparse_state_t *au)
 {
 	const char **context;
 	const char *contexts[] = { "tcontext", "scontext", NULL };
@@ -906,6 +906,230 @@ int process_avc(auparse_state_t *au)
 	return 0;
 }
 
+int process_avc_apparmor_source(auparse_state_t *au)
+{
+	uid_t uid = -1;
+	time_t time = 0;
+	struct event *avc;
+	const char *target;
+
+	/* Get the target object. */
+	if (auparse_find_field(au, "name") == NULL) {
+		if (debug) {
+			auparse_first_record(au);
+			fprintf(stderr, "Couldn't get the resource name from "
+					"the AVC record: %s\n",
+					auparse_get_record_text(au));
+		}
+		return 0;
+	}
+	target = auparse_interpret_field(au);
+
+	/* Loop backwards to find a guest session with the target object
+	 * assigned to. */
+	struct list_node_t *it;
+	struct event *res = NULL;
+	for (it = events->tail; it; it = it->prev) {
+		struct event *event = it->data;
+		if (event->success) {
+			if (event->type == ET_DOWN) {
+				/* It's just possible to find a matching guest
+				 * session in the current host session.
+				 */
+				break;
+			} else if (event->type == ET_RES &&
+			           event->end == 0 &&
+			           event->res != NULL &&
+		                   strcmp(target, event->res) == 0) {
+				res = event;
+				break;
+			}
+		}
+	}
+
+	/* Check if a resource event was found. */
+	if (res == NULL) {
+		if (debug) {
+			fprintf(stderr, "Target object not found for AVC "
+					"event.\n");
+		}
+		return 0;
+	}
+
+	if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
+		return 0;
+
+	avc = event_alloc();
+	if (avc == NULL)
+		return 1;
+	avc->type = ET_AVC;
+
+	/* Guest info */
+	avc->uuid = copy_str(res->uuid);
+	avc->name = copy_str(res->name);
+	memcpy(avc->proof, res->proof, sizeof(avc->proof));
+
+	/* AVC info */
+	avc->start = time;
+	avc->uid = uid;
+	auparse_first_record(au);
+	if (auparse_find_field(au, "apparmor")) {
+		int i;
+		avc->avc_result = copy_str(auparse_interpret_field(au));
+		for (i = 0; avc->avc_result && avc->avc_result[i]; i++) {
+			avc->avc_result[i] = tolower(avc->avc_result[i]);
+		}
+	}
+	if (auparse_find_field(au, "operation"))
+		avc->avc_operation = copy_str(auparse_interpret_field(au));
+	avc->target = copy_str(target);
+	if (auparse_find_field(au, "comm"))
+		avc->comm = copy_str(auparse_interpret_field(au));
+
+	add_proof(avc, au);
+	if (list_append(events, avc) == NULL) {
+		event_free(avc);
+		return 1;
+	}
+	return 0;
+}
+
+int process_avc_apparmor_target(auparse_state_t *au)
+{
+	uid_t uid;
+	time_t time;
+	const char *profile;
+	struct event *avc;
+
+	/* Get profile associated with the AVC record */
+	if (auparse_find_field(au, "profile") == NULL) {
+		if (debug) {
+			auparse_first_record(au);
+			fprintf(stderr, "AppArmor profile not found for AVC "
+					"record: %s\n",
+					auparse_get_record_text(au));
+		}
+		return 0;
+	}
+	profile = auparse_interpret_field(au);
+
+	/* Break path to get just the basename */
+	const char *basename = profile + strlen(profile);
+	while (basename != profile && *basename != '/')
+		basename--;
+	if (*basename == '/')
+		basename++;
+
+	/* Check if it is an apparmor profile generated by libvirt and get the
+	 * guest UUID from it */
+	const char *prefix = "libvirt-";
+	if (strncmp(prefix, basename, strlen(prefix)) != 0) {
+		if (debug) {
+			fprintf(stderr, "Found a profile which is not "
+					"generated by libvirt: %s\n", profile);
+		}
+		return 0;
+	}
+
+	/* Try to find a valid guest session */
+	const char *uuid = basename + strlen(prefix);
+	struct list_node_t *it;
+	struct event *machine_id = NULL;
+	for (it = events->tail; it; it = it->prev) {
+		struct event *event = it->data;
+		if (event->success) {
+			if (event->uuid != NULL &&
+			    strcmp(event->uuid, uuid) == 0) {
+				/* machine_id is used here instead of the start
+				 * event because it is generated before any
+				 * other event when a guest is started. So,
+				 * it's possible to correlate AVC events that
+				 * occurs during a guest start.
+				 */
+				if (event->type == ET_MACHINE_ID) {
+					machine_id = event;
+					break;
+				} else if (event->type == ET_STOP) {
+					break;
+				}
+			} else if (event->type == ET_DOWN) {
+				break;
+			}
+		}
+	}
+	if (machine_id == NULL) {
+		if (debug) {
+			fprintf(stderr, "Found an AVC record for an unknown "
+					"guest.\n");
+		}
+		return 0;
+	}
+
+	if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL))
+		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;
+	auparse_first_record(au);
+	if (auparse_find_field(au, "apparmor")) {
+		int i;
+		avc->avc_result = copy_str(auparse_interpret_field(au));
+		for (i = 0; avc->avc_result && avc->avc_result[i]; i++) {
+			avc->avc_result[i] = tolower(avc->avc_result[i]);
+		}
+	}
+	if (auparse_find_field(au, "operation"))
+		avc->avc_operation = copy_str(auparse_interpret_field(au));
+	if (auparse_find_field(au, "name"))
+		avc->target = copy_str(auparse_interpret_field(au));
+	if (auparse_find_field(au, "comm"))
+		avc->comm = copy_str(auparse_interpret_field(au));
+
+	add_proof(avc, au);
+	if (list_append(events, avc) == NULL) {
+		event_free(avc);
+		return 1;
+	}
+	return 0;
+}
+
+/* AVC records are correlated to guest through the apparmor path name. */
+int process_avc_apparmor(auparse_state_t *au)
+{
+	if (process_avc_apparmor_target(au))
+		return 1;
+	auparse_first_record(au);
+	return process_avc_apparmor_source(au);
+}
+
+int process_avc(auparse_state_t *au)
+{
+	/* Check if it is a SELinux AVC record */
+	if (auparse_find_field(au, "tcontext")) {
+		auparse_first_record(au);
+		return process_avc_selinux(au);
+	}
+
+	/* Check if it is an AppArmor AVC record */
+	auparse_first_record(au);
+	if (auparse_find_field(au, "apparmor")) {
+		auparse_first_record(au);
+		return process_avc_apparmor(au);
+	}
+	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)
-- 
1.7.1

  parent reply	other threads:[~2012-02-20 18:16 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-02-20 18:15 [PATCH 0/2] Improvements to AVC record matching Marcelo Cerri
2012-02-20 18:15 ` [PATCH 1/2] auvirt: Improve matching of AVC records generated by SELinux Marcelo Cerri
2012-02-28 23:19   ` Steve Grubb
2012-02-20 18:15 ` Marcelo Cerri [this message]
2012-02-28 23:28   ` [PATCH 2/2] auvirt: Add support for AVC records generated by AppArmor Steve Grubb

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1329761747-27905-3-git-send-email-mhcerri@linux.vnet.ibm.com \
    --to=mhcerri@linux.vnet.ibm.com \
    --cc=bryntcor@us.ibm.com \
    --cc=gcwilson@us.ibm.com \
    --cc=linux-audit@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox