[PATCH 2/2] auvirt: Add support for AVC records generated by AppArmor
Steve Grubb
sgrubb at redhat.com
Tue Feb 28 23:28:33 UTC 2012
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)
More information about the Linux-audit
mailing list