diff options
Diffstat (limited to 'security/apparmor/policy.c')
-rw-r--r-- | security/apparmor/policy.c | 61 |
1 files changed, 44 insertions, 17 deletions
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 705c2879d3a9..179e68d7dc5f 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -766,7 +766,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) struct aa_profile *profile; rcu_read_lock(); - profile = aa_get_profile(__find_child(&parent->base.profiles, name)); + do { + profile = __find_child(&parent->base.profiles, name); + } while (profile && !aa_get_profile_not0(profile)); rcu_read_unlock(); /* refcount released by caller */ @@ -916,6 +918,22 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, &sa, NULL); } +bool policy_view_capable(void) +{ + struct user_namespace *user_ns = current_user_ns(); + bool response = false; + + if (ns_capable(user_ns, CAP_MAC_ADMIN)) + response = true; + + return response; +} + +bool policy_admin_capable(void) +{ + return policy_view_capable() && !aa_g_lock_policy; +} + /** * aa_may_manage_policy - can the current task manage policy * @op: the policy manipulation operation being done @@ -930,7 +948,7 @@ bool aa_may_manage_policy(int op) return 0; } - if (!capable(CAP_MAC_ADMIN)) { + if (!policy_admin_capable()) { audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); return 0; } @@ -1067,7 +1085,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, */ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) { - const char *ns_name, *name = NULL, *info = NULL; + const char *ns_name, *info = NULL; struct aa_namespace *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; @@ -1082,18 +1100,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* released below */ ns = aa_prepare_namespace(ns_name); if (!ns) { - info = "failed to prepare namespace"; - error = -ENOMEM; - name = ns_name; - goto fail; + error = audit_policy(op, GFP_KERNEL, ns_name, + "failed to prepare namespace", -ENOMEM); + goto free; } mutex_lock(&ns->lock); /* setup parent and ns info */ list_for_each_entry(ent, &lh, list) { struct aa_policy *policy; - - name = ent->new->base.hname; error = __lookup_replace(ns, ent->new->base.hname, noreplace, &ent->old, &info); if (error) @@ -1121,7 +1136,6 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) if (!p) { error = -ENOENT; info = "parent does not exist"; - name = ent->new->base.hname; goto fail_lock; } rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); @@ -1163,7 +1177,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); + audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); if (ent->old) { __replace_profile(ent->old, ent->new, 1); @@ -1187,14 +1201,14 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* parent replaced in this atomic set? */ if (newest != parent) { aa_get_profile(newest); - aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); - } else - aa_put_profile(newest); + aa_put_profile(parent); + } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); - __list_add_profile(&parent->base.profiles, ent->new); + __list_add_profile(&newest->base.profiles, ent->new); + aa_put_profile(newest); } else { /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, @@ -1214,9 +1228,22 @@ out: fail_lock: mutex_unlock(&ns->lock); -fail: - error = audit_policy(op, GFP_KERNEL, name, info, error); + /* audit cause of failure */ + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, 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"; + list_for_each_entry(tmp, &lh, list) { + if (tmp == ent) { + info = "unchecked profile in failed atomic policy load"; + /* skip entry that caused failure */ + continue; + } + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, 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); |