Merge remote-tracking branches 'regulator/fix/email' and 'regulator/fix/qcom-smd...
[cascardo/linux.git] / security / apparmor / policy.c
index 705c287..179e68d 100644 (file)
@@ -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);