On Monday, February 20, 2012 01:15:47 PM Marcelo Cerri wrote:
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. ---
I added #ifdef WITH_APPARMOR in a couple places. You might want to check that it
still works as expected.
-Steve
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)