Skip to content

Commit 665b185

Browse files
committed
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: John Johansen <john.johansen@canonical.com>
1 parent 58f89ce commit 665b185

1 file changed

Lines changed: 78 additions & 9 deletions

File tree

security/apparmor/policy.c

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,57 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns,
423423
return &profile->base;
424424
}
425425

426+
/**
427+
* __create_missing_ancestors - create place holders for missing ancestores
428+
* @ns: namespace to lookup profile in (NOT NULL)
429+
* @hname: hierarchical profile name to find parent of (NOT NULL)
430+
* @gfp: type of allocation.
431+
*
432+
* Returns: NULL on error, parent profile on success
433+
*
434+
* Requires: ns mutex lock held
435+
*
436+
* Returns: unrefcounted parent policy or NULL if error creating
437+
* place holder profiles.
438+
*/
439+
static struct aa_policy *__create_missing_ancestors(struct aa_ns *ns,
440+
const char *hname,
441+
gfp_t gfp)
442+
{
443+
struct aa_policy *policy;
444+
struct aa_profile *parent, *profile = NULL;
445+
char *split;
446+
447+
AA_BUG(!ns);
448+
AA_BUG(!hname);
449+
450+
policy = &ns->base;
451+
452+
for (split = strstr(hname, "//"); split;) {
453+
parent = profile;
454+
profile = __strn_find_child(&policy->profiles, hname,
455+
split - hname);
456+
if (!profile) {
457+
const char *name = kstrndup(hname, split - hname,
458+
gfp);
459+
if (!name)
460+
return NULL;
461+
profile = aa_alloc_null(parent, name, gfp);
462+
kfree(name);
463+
if (!profile)
464+
return NULL;
465+
if (!parent)
466+
profile->ns = aa_get_ns(ns);
467+
}
468+
policy = &profile->base;
469+
hname = split + 2;
470+
split = strstr(hname, "//");
471+
}
472+
if (!profile)
473+
return &ns->base;
474+
return &profile->base;
475+
}
476+
426477
/**
427478
* __lookupn_profile - lookup the profile matching @hname
428479
* @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,
10321083
/* setup parent and ns info */
10331084
list_for_each_entry(ent, &lh, list) {
10341085
struct aa_policy *policy;
1086+
struct aa_profile *p;
10351087

10361088
if (aa_g_export_binary)
10371089
ent->new->rawdata = aa_get_loaddata(udata);
@@ -1056,21 +1108,38 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
10561108
continue;
10571109

10581110
/* no ref on policy only use inside lock */
1111+
p = NULL;
10591112
policy = __lookup_parent(ns, ent->new->base.hname);
10601113
if (!policy) {
1061-
struct aa_profile *p;
1114+
/* first check for parent in the load set */
10621115
p = __list_lookup_parent(&lh, ent->new);
10631116
if (!p) {
1064-
error = -ENOENT;
1065-
info = "parent does not exist";
1066-
goto fail_lock;
1117+
/*
1118+
* fill in missing parent with null
1119+
* profile that doesn't have
1120+
* permissions. This allows for
1121+
* individual profile loading where
1122+
* the child is loaded before the
1123+
* parent, and outside of the current
1124+
* atomic set. This unfortunately can
1125+
* happen with some userspaces. The
1126+
* null profile will be replaced once
1127+
* the parent is loaded.
1128+
*/
1129+
policy = __create_missing_ancestors(ns,
1130+
ent->new->base.hname,
1131+
GFP_KERNEL);
1132+
if (!policy) {
1133+
error = -ENOENT;
1134+
info = "parent does not exist";
1135+
goto fail_lock;
1136+
}
10671137
}
1068-
rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
1069-
} else if (policy != &ns->base) {
1070-
/* released on profile replacement or free_profile */
1071-
struct aa_profile *p = (struct aa_profile *) policy;
1072-
rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
10731138
}
1139+
if (!p && policy != &ns->base)
1140+
/* released on profile replacement or free_profile */
1141+
p = (struct aa_profile *) policy;
1142+
rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
10741143
}
10751144

10761145
/* create new fs entries for introspection if needed */

0 commit comments

Comments
 (0)