Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04dc715e authored by John Johansen's avatar John Johansen
Browse files

apparmor: audit policy ns specified in policy load



Verify that profiles in a load set specify the same policy ns and
audit the name of the policy ns that policy is being loaded for.

Signed-off-by: default avatarJohn Johansen <john.johansen@canonical.com>
parent 5ac8c355
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ struct aa_load_ent {
	struct aa_profile *new;
	struct aa_profile *old;
	struct aa_profile *rename;
	const char *ns_name;
};

void aa_load_ent_free(struct aa_load_ent *ent);
+40 −14
Original line number Diff line number Diff line
@@ -819,7 +819,7 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
	struct aa_ns *ns = NULL;
	struct aa_load_ent *ent, *tmp;
	int op = OP_PROF_REPL;
	ssize_t error;
	ssize_t count, error;
	LIST_HEAD(lh);

	/* released below */
@@ -827,14 +827,40 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
	if (error)
		goto out;

	/* released below */
	/* ensure that profiles are all for the same ns
	 * TODO: update locking to remove this constaint. All profiles in
	 *       the load set must succeed as a set or the load will
	 *       fail. Sort ent list and take ns locks in hierarchy order
	 */
	count = 0;
	list_for_each_entry(ent, &lh, list) {
		if (ns_name) {
			if (ent->ns_name &&
			    strcmp(ent->ns_name, ns_name) != 0) {
				info = "policy load has mixed namespaces";
				error = -EACCES;
				goto fail;
			}
		} else if (ent->ns_name) {
			if (count) {
				info = "policy load has mixed namespaces";
				error = -EACCES;
				goto fail;
			}
			ns_name = ent->ns_name;
		} else
			count++;
	}
	if (ns_name) {
		ns = aa_prepare_ns(view, ns_name);
	if (!ns) {
		error = audit_policy(__aa_current_profile(), op, GFP_KERNEL,
				     NULL, ns_name,
				     "failed to prepare namespace", -ENOMEM);
		goto free;
		if (IS_ERR(ns)) {
			info = "failed to prepare namespace";
			error = PTR_ERR(ns);
			ns = NULL;
			goto fail;
		}
	} else
		ns = aa_get_ns(view);

	mutex_lock(&ns->lock);
	/* setup parent and ns info */
@@ -964,7 +990,8 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,

	/* audit cause of failure */
	op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
	audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL,
fail:
	audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name,
		     ent->new->base.hname, info, error);
	/* audit status that rest of profiles in the atomic set failed too */
	info = "valid profile in failed atomic policy load";
@@ -975,10 +1002,9 @@ ssize_t aa_replace_profiles(struct aa_ns *view, bool noreplace,
			continue;
		}
		op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL;
		audit_policy(__aa_current_profile(), op, GFP_KERNEL, NULL,
		audit_policy(__aa_current_profile(), op, GFP_KERNEL, ns_name,
			     tmp->new->base.hname, info, error);
	}
free:
	list_for_each_entry_safe(ent, tmp, &lh, list) {
		list_del_init(&ent->list);
		aa_load_ent_free(ent);
@@ -1005,6 +1031,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size)
	struct aa_ns *root = NULL, *ns = NULL;
	struct aa_profile *profile = NULL;
	const char *name = fqname, *info = NULL;
	char *ns_name = NULL;
	ssize_t error = 0;

	if (*fqname == 0) {
@@ -1016,7 +1043,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size)
	root = view;

	if (fqname[0] == ':') {
		char *ns_name;
		name = aa_split_fqname(fqname, &ns_name);
		/* released below */
		ns = aa_find_ns(root, ns_name);
@@ -1050,7 +1076,7 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size)

	/* don't fail removal if audit fails */
	(void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL,
			    NULL, name, info, error);
			    ns_name, name, info, error);
	aa_put_ns(ns);
	aa_put_profile(profile);
	return size;
@@ -1061,6 +1087,6 @@ ssize_t aa_remove_profiles(struct aa_ns *view, char *fqname, size_t size)

fail:
	(void) audit_policy(__aa_current_profile(), OP_PROF_RM, GFP_KERNEL,
			    NULL, name, info, error);
			    ns_name, name, info, error);
	return error;
}
+36 −10
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
/**
 * audit_iface - do audit message for policy unpacking/load/replace/remove
 * @new: profile if it has been allocated (MAYBE NULL)
 * @ns_name: name of the ns the profile is to be loaded to (MAY BE NULL)
 * @name: name of the profile being manipulated (MAYBE NULL)
 * @info: any extra info about the failure (MAYBE NULL)
 * @e: buffer position info
@@ -98,14 +99,16 @@ static void audit_cb(struct audit_buffer *ab, void *va)
 *
 * Returns: %0 or error
 */
static int audit_iface(struct aa_profile *new, const char *name,
		       const char *info, struct aa_ext *e, int error)
static int audit_iface(struct aa_profile *new, const char *ns_name,
		       const char *name, const char *info, struct aa_ext *e,
		       int error)
{
	struct aa_profile *profile = __aa_current_profile();
	struct common_audit_data sa;
	struct apparmor_audit_data aad = {0,};
	sa.type = LSM_AUDIT_DATA_NONE;
	sa.aad = &aad;
	aad.iface.ns = ns_name;
	if (e)
		aad.iface.pos = e->pos - e->start;
	aad.iface.target = new;
@@ -486,19 +489,32 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile)
 *
 * NOTE: unpack profile sets audit struct if there is a failure
 */
static struct aa_profile *unpack_profile(struct aa_ext *e)
static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
{
	struct aa_profile *profile = NULL;
	const char *name = NULL;
	const char *tmpname, *tmpns = NULL, *name = NULL;
	size_t ns_len;
	int i, error = -EPROTO;
	kernel_cap_t tmpcap;
	u32 tmp;

	*ns_name = NULL;

	/* check that we have the right struct being passed */
	if (!unpack_nameX(e, AA_STRUCT, "profile"))
		goto fail;
	if (!unpack_str(e, &name, NULL))
		goto fail;
	if (*name == '\0')
		goto fail;

	tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
	if (tmpns) {
		*ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
		if (!*ns_name)
			goto fail;
		name = tmpname;
	}

	profile = aa_alloc_profile(name, GFP_KERNEL);
	if (!profile)
@@ -646,7 +662,8 @@ static struct aa_profile *unpack_profile(struct aa_ext *e)
		name = NULL;
	else if (!name)
		name = "unknown";
	audit_iface(profile, name, "failed to unpack profile", e, error);
	audit_iface(profile, NULL, name, "failed to unpack profile", e,
		    error);
	aa_free_profile(profile);

	return ERR_PTR(error);
@@ -669,7 +686,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
	/* get the interface version */
	if (!unpack_u32(e, &e->version, "version")) {
		if (required) {
			audit_iface(NULL, NULL, "invalid profile format",
			audit_iface(NULL, NULL, NULL, "invalid profile format",
				    e, error);
			return error;
		}
@@ -680,15 +697,21 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
	 * Mask off everything that is not kernel abi version
	 */
	if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) {
		audit_iface(NULL, NULL, "unsupported interface version",
		audit_iface(NULL, NULL, NULL, "unsupported interface version",
			    e, error);
		return error;
	}

	/* read the namespace if present */
	if (unpack_str(e, &name, "namespace")) {
		if (*name == '\0') {
			audit_iface(NULL, NULL, NULL, "invalid namespace name",
				    e, error);
			return error;
		}
		if (*ns && strcmp(*ns, name))
			audit_iface(NULL, NULL, "invalid ns change", e, error);
			audit_iface(NULL, NULL, NULL, "invalid ns change", e,
				    error);
		else if (!*ns)
			*ns = name;
	}
@@ -730,7 +753,7 @@ static int verify_profile(struct aa_profile *profile)
	if (profile->file.dfa &&
	    !verify_dfa_xindex(profile->file.dfa,
			       profile->file.trans.size)) {
		audit_iface(profile, NULL, "Invalid named transition",
		audit_iface(profile, NULL, NULL, "Invalid named transition",
			    NULL, -EPROTO);
		return -EPROTO;
	}
@@ -744,6 +767,7 @@ void aa_load_ent_free(struct aa_load_ent *ent)
		aa_put_profile(ent->rename);
		aa_put_profile(ent->old);
		aa_put_profile(ent->new);
		kfree(ent->ns_name);
		kzfree(ent);
	}
}
@@ -782,13 +806,14 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,

	*ns = NULL;
	while (e.pos < e.end) {
		char *ns_name = NULL;
		void *start;
		error = verify_header(&e, e.pos == e.start, ns);
		if (error)
			goto fail;

		start = e.pos;
		profile = unpack_profile(&e);
		profile = unpack_profile(&e, &ns_name);
		if (IS_ERR(profile)) {
			error = PTR_ERR(profile);
			goto fail;
@@ -810,6 +835,7 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
		}

		ent->new = profile;
		ent->ns_name = ns_name;
		list_add_tail(&ent->list, lh);
	}
	udata->abi = e.version & K_ABI_MASK;