From mboxrd@z Thu Jan 1 00:00:00 1970 From: Steve Grubb Subject: Re: [PATCH 2/2] auvirt: Add support for AVC records generated by AppArmor Date: Tue, 28 Feb 2012 18:28:33 -0500 Message-ID: <201202281828.34194.sgrubb@redhat.com> References: <1329761747-27905-1-git-send-email-mhcerri@linux.vnet.ibm.com> <1329761747-27905-3-git-send-email-mhcerri@linux.vnet.ibm.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1329761747-27905-3-git-send-email-mhcerri@linux.vnet.ibm.com> List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-audit-bounces@redhat.com Errors-To: linux-audit-bounces@redhat.com To: linux-audit@redhat.com Cc: gcwilson@us.ibm.com, bryntcor@us.ibm.com List-Id: linux-audit@redhat.com 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)