Collect the self caps and the children template into struct aa_ns_capset, so a policy load stages, checks and commits one tentative copy of the whole set.
Signed-off-by: Maxime Bélair <[email protected]> --- security/apparmor/apparmorfs.c | 12 ++--- security/apparmor/include/policy_ns.h | 27 +++++++--- security/apparmor/policy.c | 16 +++--- security/apparmor/policy_ns.c | 72 +++++++++++++++++---------- 4 files changed, 79 insertions(+), 48 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 27962b51002b..98029e7f9d29 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -1494,12 +1494,12 @@ SEQ_NS_FOPS(NAME) SEQ_NS_ACCT(acct_count, atomic_long_read(&ns->acct.profile_count)); SEQ_NS_ACCT(acct_size, atomic_long_read(&ns->acct.resident)); -SEQ_NS_ACCT(acct_max_count, ns->acct.limits.profiles); -SEQ_NS_ACCT(acct_max_size, ns->acct.limits.memory); -SEQ_NS_ACCT(acct_max_profile, ns->acct.limits.max_profile); -SEQ_NS_ACCT(acct_namespaces, ns->acct.limits.namespaces); -SEQ_NS_ACCT(acct_depth, ns->acct.limits.depth); -SEQ_NS_ACCT(acct_criu, ns->acct.limits.criu); +SEQ_NS_ACCT(acct_max_count, ns->acct.caps.limits.profiles); +SEQ_NS_ACCT(acct_max_size, ns->acct.caps.limits.memory); +SEQ_NS_ACCT(acct_max_profile, ns->acct.caps.limits.max_profile); +SEQ_NS_ACCT(acct_namespaces, ns->acct.caps.limits.namespaces); +SEQ_NS_ACCT(acct_depth, ns->acct.caps.limits.depth); +SEQ_NS_ACCT(acct_criu, ns->acct.caps.limits.criu); /* policy/raw_data/ * file ops */ diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 4248b1f3fbf8..dce753feb91f 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -28,17 +28,33 @@ struct apparmor_audit_data; #define AA_NS_QUOTA_RATELIMIT_INTERVAL (5 * HZ) #define AA_NS_QUOTA_RATELIMIT_BURST 10 -/* struct aa_ns_acct - per-namespace resource accounting and caps +/* struct aa_ns_capset - the standing caps a namespace enforces and stamps * @limits: caps enforced against this namespace (self) * @child: template caps stamped onto namespaces this namespace creates (children) + * @child_percent: percentage (0-100) of the parent's cap, resolved per child at creation + */ +struct aa_ns_capset { + struct aa_ns_caps limits; + struct aa_ns_caps child; + u32 child_percent; +}; + +static inline void aa_ns_capset_init_unset(struct aa_ns_capset *caps) +{ + aa_ns_caps_init_unset(&caps->limits); + aa_ns_caps_init_unset(&caps->child); + caps->child_percent = 0; +} + +/* struct aa_ns_acct - per-namespace resource accounting and caps + * @caps: the caps enforced against and stamped by this namespace * @resident: current resident policy bytes charged to this ns (local scope) * @profile_count: current count of non-null profiles in this ns (local) * @ns_count: current number of direct child namespaces * @ratelimit: bounds OP_NS_QUOTA audit emission */ struct aa_ns_acct { - struct aa_ns_caps limits; - struct aa_ns_caps child; + struct aa_ns_capset caps; atomic_long_t resident; atomic_long_t profile_count; atomic_long_t ns_count; @@ -130,11 +146,10 @@ struct aa_load_ent; int aa_ns_admit_load_set(struct aa_ns *ns, struct list_head *lh, struct aa_ns_caps *limits, struct aa_loaddata *udata, struct aa_load_ent **fail_ent, const char **info); -/* apply one parsed "policyns limits" block to a (tentative) caps pair; +/* apply one parsed "policyns limits" block to a (tentative) capset; * returns -EOPNOTSUPP for a construct this kernel does not yet enforce */ -int aa_ns_apply_budget(struct aa_ns_caps *limits, struct aa_ns_caps *child, - struct aa_ns_budget *budget); +int aa_ns_apply_budget(struct aa_ns_capset *caps, struct aa_ns_budget *budget); /* mediate the policyns permission rule (create/load/replace/remove) */ int aa_policyns_perm(struct aa_label *label, struct aa_ns *target, u32 request, const char *op); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 67e63a31641a..cef057b98b27 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1275,7 +1275,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, struct aa_ns *ns = NULL; struct aa_load_ent *ent, *tmp; struct aa_loaddata *rawdata_ent; - struct aa_ns_caps pend_limits, pend_child; + struct aa_ns_capset pend_caps; const char *op; ssize_t count, error; LIST_HEAD(lh); @@ -1341,9 +1341,8 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, } mutex_lock_nested(&ns->lock, ns->level); - /* Tentative copies of the ns caps */ - pend_limits = ns->acct.limits; - pend_child = ns->acct.child; + /* Tentative copy of the ns caps */ + pend_caps = ns->acct.caps; /* check for duplicate rawdata blobs: space and file dedup */ if (!list_empty(&ns->rawdata_list)) { list_for_each_entry(rawdata_ent, &ns->rawdata_list, list) { @@ -1435,8 +1434,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, int b; for (b = 0; b < ent->new->n_budgets; b++) { - error = aa_ns_apply_budget(&pend_limits, - &pend_child, + error = aa_ns_apply_budget(&pend_caps, &ent->new->budgets[b]); if (error) { info = "policyns limits: unsupported construct"; @@ -1460,7 +1458,8 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, * Admission: check the whole load set against the tentative caps * before installing anything, so a breach rejects the whole set. */ - error = aa_ns_admit_load_set(ns, &lh, &pend_limits, udata, &ent, &info); + error = aa_ns_admit_load_set(ns, &lh, &pend_caps.limits, udata, &ent, + &info); if (error) goto fail_lock; @@ -1493,8 +1492,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label, /* Done with checks that may fail - do actual replacement */ /* commit the caps the load was admitted against */ - ns->acct.limits = pend_limits; - ns->acct.child = pend_child; + ns->acct.caps = pend_caps; __aa_bump_ns_revision(ns); if (aa_g_export_binary) __aa_loaddata_update(udata, ns->revision); diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c index ee29ca54b7a8..fb67557548b8 100644 --- a/security/apparmor/policy_ns.c +++ b/security/apparmor/policy_ns.c @@ -206,8 +206,7 @@ void aa_ns_acct_init(struct aa_ns *ns) { struct aa_ns_acct *acct = &ns->acct; - aa_ns_caps_init_unset(&acct->limits); - aa_ns_caps_init_unset(&acct->child); + aa_ns_capset_init_unset(&acct->caps); atomic_long_set(&acct->resident, 0); atomic_long_set(&acct->profile_count, 0); atomic_long_set(&acct->ns_count, 0); @@ -298,7 +297,7 @@ static int cap_admit_delta(struct aa_ns *ns, enum aa_policyns_key key, */ int aa_ns_admit_create(struct aa_ns *parent) { - struct aa_ns_caps *pl = &parent->acct.limits; + struct aa_ns_caps *pl = &parent->acct.caps.limits; int error; if (!aa_g_policy_ns_quota) @@ -519,9 +518,12 @@ void aa_ns_uncharge_profile(struct aa_profile *profile) * @dst: destination caps, treated as a long array in wire-key order * @b: parsed budget block * @tighten: cap_min() against the existing value (self) vs overwrite (children) + * + * Percentage values are stored raw and are resolved against the parent's + * cap when a child is created (avoiding load-order dependence). */ static void apply_budget_keys(struct aa_ns_caps *dst, struct aa_ns_budget *b, - bool tighten, const struct aa_ns_caps *pct_base) + bool tighten) { long *cap = (long *)dst; int k; @@ -531,23 +533,17 @@ static void apply_budget_keys(struct aa_ns_caps *dst, struct aa_ns_budget *b, if (!(b->specified & (1u << k))) continue; - if (b->percent & (1u << k)) - /* N% of the parent's own cap for key @k */ - v = cap_percent(((const long *)pct_base)[k], b->values[k]); - else - v = b->values[k]; + v = b->values[k]; cap[k] = tighten ? cap_min(cap[k], v) : v; } } /** - * aa_ns_apply_budget - apply one parsed "policyns limits" block to a caps pair - * @limits: the (tentative) self caps of the namespace the load targets - * @child: the (tentative) children template of that namespace + * aa_ns_apply_budget - apply one parsed "policyns limits" block to a capset + * @caps: the (tentative) capset of the namespace the load targets * @b: one parsed budget block */ -int aa_ns_apply_budget(struct aa_ns_caps *limits, struct aa_ns_caps *child, - struct aa_ns_budget *b) +int aa_ns_apply_budget(struct aa_ns_capset *caps, struct aa_ns_budget *b) { /* Some features remains to be implemented and are rejected with -EOPNOTSUPP. */ if (b->scope == AA_POLICYNS_SCOPE_SUBTREE) @@ -560,38 +556,60 @@ int aa_ns_apply_budget(struct aa_ns_caps *limits, struct aa_ns_caps *child, case AA_POLICYNS_TGT_SELF: if (b->percent) /* % is a per-child ratio only */ return -EOPNOTSUPP; - apply_budget_keys(limits, b, true, NULL); + apply_budget_keys(&caps->limits, b, true); return 0; case AA_POLICYNS_TGT_CHILDREN: - aa_ns_caps_init_unset(child); - apply_budget_keys(child, b, false, limits); + /* the block is the template: last children block wins */ + aa_ns_caps_init_unset(&caps->child); + apply_budget_keys(&caps->child, b, false); + caps->child_percent = b->percent; return 0; default: /* descendants/root/:NAME: not yet enforced */ return -EOPNOTSUPP; } } +/* resolve_percent_caps - resolve raw percentage keys against @base's caps */ +static void resolve_percent_caps(struct aa_ns_caps *caps, u32 percent, + const struct aa_ns_caps *base) +{ + long *cap = (long *)caps; + const long *b = (const long *)base; + int k; + + for (k = 0; k < AA_POLICYNS_KEY_MAX; k++) + if (percent & (1u << k)) + cap[k] = cap_percent(b[k], cap[k]); +} + /* inherit_child_caps - compute a new child's caps from @parent's template */ static void inherit_child_caps(struct aa_ns *child, struct aa_ns *parent) { - struct aa_ns_caps *t = &parent->acct.child; - struct aa_ns_caps *pl = &parent->acct.limits; - struct aa_ns_caps *cl = &child->acct.limits; + struct aa_ns_caps *pl = &parent->acct.caps.limits; + struct aa_ns_caps *cl = &child->acct.caps.limits; struct aa_ns_acct *pa = &parent->acct; + struct aa_ns_caps t = parent->acct.caps.child; + + /* + * Resolve percentage keys against the parent's cap in a local copy of + * the template so every child gets the ratio of the parent's caps as + * they stand at its creation. + */ + resolve_percent_caps(&t, parent->acct.caps.child_percent, pl); - cl->memory = cap_min(t->memory, + cl->memory = cap_min(t.memory, cap_remaining(pl->memory, atomic_long_read(&pa->resident))); - cl->profiles = cap_min(t->profiles, + cl->profiles = cap_min(t.profiles, cap_remaining(pl->profiles, atomic_long_read(&pa->profile_count))); - cl->namespaces = cap_min(t->namespaces, + cl->namespaces = cap_min(t.namespaces, cap_remaining(pl->namespaces, atomic_long_read(&pa->ns_count))); - cl->max_profile = cap_min(t->max_profile, pl->max_profile); - cl->criu = cap_min(t->criu, pl->criu); - cl->load_rate = cap_min(t->load_rate, pl->load_rate); - cl->depth = cap_min(t->depth, cap_dec(pl->depth)); + cl->max_profile = cap_min(t.max_profile, pl->max_profile); + cl->criu = cap_min(t.criu, pl->criu); + cl->load_rate = cap_min(t.load_rate, pl->load_rate); + cl->depth = cap_min(t.depth, cap_dec(pl->depth)); } /* -- 2.51.0
