[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