Commit 665b1856 authored by John Johansen's avatar John Johansen

apparmor: Fix loading of child before parent

Unfortunately it is possible for some userspace's to load children
profiles before the parent profile. This can even happen when the
child and the parent are in different load sets.

Fix this by creating a null place holder profile that grants no permissions
and can be replaced by the parent once it is loaded.
Signed-off-by: default avatarJohn Johansen <john.johansen@canonical.com>
parent 58f89ce5
...@@ -423,6 +423,57 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns, ...@@ -423,6 +423,57 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns,
return &profile->base; return &profile->base;
} }
/**
* __create_missing_ancestors - create place holders for missing ancestores
* @ns: namespace to lookup profile in (NOT NULL)
* @hname: hierarchical profile name to find parent of (NOT NULL)
* @gfp: type of allocation.
*
* Returns: NULL on error, parent profile on success
*
* Requires: ns mutex lock held
*
* Returns: unrefcounted parent policy or NULL if error creating
* place holder profiles.
*/
static struct aa_policy *__create_missing_ancestors(struct aa_ns *ns,
const char *hname,
gfp_t gfp)
{
struct aa_policy *policy;
struct aa_profile *parent, *profile = NULL;
char *split;
AA_BUG(!ns);
AA_BUG(!hname);
policy = &ns->base;
for (split = strstr(hname, "//"); split;) {
parent = profile;
profile = __strn_find_child(&policy->profiles, hname,
split - hname);
if (!profile) {
const char *name = kstrndup(hname, split - hname,
gfp);
if (!name)
return NULL;
profile = aa_alloc_null(parent, name, gfp);
kfree(name);
if (!profile)
return NULL;
if (!parent)
profile->ns = aa_get_ns(ns);
}
policy = &profile->base;
hname = split + 2;
split = strstr(hname, "//");
}
if (!profile)
return &ns->base;
return &profile->base;
}
/** /**
* __lookupn_profile - lookup the profile matching @hname * __lookupn_profile - lookup the profile matching @hname
* @base: base list to start looking up profile name from (NOT NULL) * @base: base list to start looking up profile name from (NOT NULL)
...@@ -1032,6 +1083,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, ...@@ -1032,6 +1083,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
/* setup parent and ns info */ /* setup parent and ns info */
list_for_each_entry(ent, &lh, list) { list_for_each_entry(ent, &lh, list) {
struct aa_policy *policy; struct aa_policy *policy;
struct aa_profile *p;
if (aa_g_export_binary) if (aa_g_export_binary)
ent->new->rawdata = aa_get_loaddata(udata); ent->new->rawdata = aa_get_loaddata(udata);
...@@ -1056,22 +1108,39 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, ...@@ -1056,22 +1108,39 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
continue; continue;
/* no ref on policy only use inside lock */ /* no ref on policy only use inside lock */
p = NULL;
policy = __lookup_parent(ns, ent->new->base.hname); policy = __lookup_parent(ns, ent->new->base.hname);
if (!policy) { if (!policy) {
struct aa_profile *p; /* first check for parent in the load set */
p = __list_lookup_parent(&lh, ent->new); p = __list_lookup_parent(&lh, ent->new);
if (!p) { if (!p) {
/*
* fill in missing parent with null
* profile that doesn't have
* permissions. This allows for
* individual profile loading where
* the child is loaded before the
* parent, and outside of the current
* atomic set. This unfortunately can
* happen with some userspaces. The
* null profile will be replaced once
* the parent is loaded.
*/
policy = __create_missing_ancestors(ns,
ent->new->base.hname,
GFP_KERNEL);
if (!policy) {
error = -ENOENT; error = -ENOENT;
info = "parent does not exist"; info = "parent does not exist";
goto fail_lock; goto fail_lock;
} }
rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); }
} else if (policy != &ns->base) { }
if (!p && policy != &ns->base)
/* released on profile replacement or free_profile */ /* released on profile replacement or free_profile */
struct aa_profile *p = (struct aa_profile *) policy; p = (struct aa_profile *) policy;
rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
} }
}
/* create new fs entries for introspection if needed */ /* create new fs entries for introspection if needed */
if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary) { if (!udata->dents[AAFS_LOADDATA_DIR] && aa_g_export_binary) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment