1 commit 665b1856dc2399828d8ee07a18d4fd79868e729a
2 Author: John Johansen <john.johansen@canonical.com>
3 Date: Mon Oct 3 06:06:26 2022 -0700
5 apparmor: Fix loading of child before parent
7 Unfortunately it is possible for some userspace's to load children
8 profiles before the parent profile. This can even happen when the
9 child and the parent are in different load sets.
11 Fix this by creating a null place holder profile that grants no permissions
12 and can be replaced by the parent once it is loaded.
14 Signed-off-by: John Johansen <john.johansen@canonical.com>
16 diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
17 index c17ccedd35f1..66034cf96f4c 100644
18 --- a/security/apparmor/policy.c
19 +++ b/security/apparmor/policy.c
20 @@ -423,6 +423,57 @@ static struct aa_policy *__lookup_parent(struct aa_ns *ns,
21 return &profile->base;
25 + * __create_missing_ancestors - create place holders for missing ancestores
26 + * @ns: namespace to lookup profile in (NOT NULL)
27 + * @hname: hierarchical profile name to find parent of (NOT NULL)
28 + * @gfp: type of allocation.
30 + * Returns: NULL on error, parent profile on success
32 + * Requires: ns mutex lock held
34 + * Returns: unrefcounted parent policy or NULL if error creating
35 + * place holder profiles.
37 +static struct aa_policy *__create_missing_ancestors(struct aa_ns *ns,
41 + struct aa_policy *policy;
42 + struct aa_profile *parent, *profile = NULL;
50 + for (split = strstr(hname, "//"); split;) {
52 + profile = __strn_find_child(&policy->profiles, hname,
55 + const char *name = kstrndup(hname, split - hname,
59 + profile = aa_alloc_null(parent, name, gfp);
64 + profile->ns = aa_get_ns(ns);
66 + policy = &profile->base;
68 + split = strstr(hname, "//");
72 + return &profile->base;
76 * __lookupn_profile - lookup the profile matching @hname
77 * @base: base list to start looking up profile name from (NOT NULL)
78 @@ -1032,6 +1083,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
79 /* setup parent and ns info */
80 list_for_each_entry(ent, &lh, list) {
81 struct aa_policy *policy;
82 + struct aa_profile *p;
84 if (aa_g_export_binary)
85 ent->new->rawdata = aa_get_loaddata(udata);
86 @@ -1056,21 +1108,38 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
89 /* no ref on policy only use inside lock */
91 policy = __lookup_parent(ns, ent->new->base.hname);
93 - struct aa_profile *p;
94 + /* first check for parent in the load set */
95 p = __list_lookup_parent(&lh, ent->new);
98 - info = "parent does not exist";
101 + * fill in missing parent with null
102 + * profile that doesn't have
103 + * permissions. This allows for
104 + * individual profile loading where
105 + * the child is loaded before the
106 + * parent, and outside of the current
107 + * atomic set. This unfortunately can
108 + * happen with some userspaces. The
109 + * null profile will be replaced once
110 + * the parent is loaded.
112 + policy = __create_missing_ancestors(ns,
113 + ent->new->base.hname,
117 + info = "parent does not exist";
121 - rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
122 - } else if (policy != &ns->base) {
123 - /* released on profile replacement or free_profile */
124 - struct aa_profile *p = (struct aa_profile *) policy;
125 - rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
127 + if (!p && policy != &ns->base)
128 + /* released on profile replacement or free_profile */
129 + p = (struct aa_profile *) policy;
130 + rcu_assign_pointer(ent->new->parent, aa_get_profile(p));
133 /* create new fs entries for introspection if needed */