Merge tag 'apparmor-pr-2021-11-10' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / security / apparmor / policy.c
index 4c010c9a6af1d35adc71a6552cba85f01f04b281..b0cbc4906cb3b1618c3efa0def707173601cec99 100644 (file)
@@ -260,8 +260,7 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
        struct aa_profile *profile;
 
        /* freed by free_profile - usually through aa_put_profile */
-       profile = kzalloc(sizeof(*profile) + sizeof(struct aa_profile *) * 2,
-                         gfp);
+       profile = kzalloc(struct_size(profile, label.vec, 2), gfp);
        if (!profile)
                return NULL;
 
@@ -632,18 +631,35 @@ static int audit_policy(struct aa_label *label, const char *op,
        return error;
 }
 
+/* don't call out to other LSMs in the stack for apparmor policy admin
+ * permissions
+ */
+static int policy_ns_capable(struct aa_label *label,
+                            struct user_namespace *userns, int cap)
+{
+       int err;
+
+       /* check for MAC_ADMIN cap in cred */
+       err = cap_capable(current_cred(), userns, cap, CAP_OPT_NONE);
+       if (!err)
+               err = aa_capable(label, cap, CAP_OPT_NONE);
+
+       return err;
+}
+
 /**
- * policy_view_capable - check if viewing policy in at @ns is allowed
- * ns: namespace being viewed by current task (may be NULL)
+ * aa_policy_view_capable - check if viewing policy in at @ns is allowed
+ * label: label that is trying to view policy in ns
+ * ns: namespace being viewed by @label (may be NULL if @label's ns)
  * Returns: true if viewing policy is allowed
  *
  * If @ns is NULL then the namespace being viewed is assumed to be the
  * tasks current namespace.
  */
-bool policy_view_capable(struct aa_ns *ns)
+bool aa_policy_view_capable(struct aa_label *label, struct aa_ns *ns)
 {
        struct user_namespace *user_ns = current_user_ns();
-       struct aa_ns *view_ns = aa_get_current_ns();
+       struct aa_ns *view_ns = labels_view(label);
        bool root_in_user_ns = uid_eq(current_euid(), make_kuid(user_ns, 0)) ||
                               in_egroup_p(make_kgid(user_ns, 0));
        bool response = false;
@@ -655,20 +671,44 @@ bool policy_view_capable(struct aa_ns *ns)
             (unprivileged_userns_apparmor_policy != 0 &&
              user_ns->level == view_ns->level)))
                response = true;
-       aa_put_ns(view_ns);
 
        return response;
 }
 
-bool policy_admin_capable(struct aa_ns *ns)
+bool aa_policy_admin_capable(struct aa_label *label, struct aa_ns *ns)
 {
        struct user_namespace *user_ns = current_user_ns();
-       bool capable = ns_capable(user_ns, CAP_MAC_ADMIN);
+       bool capable = policy_ns_capable(label, user_ns, CAP_MAC_ADMIN) == 0;
 
        AA_DEBUG("cap_mac_admin? %d\n", capable);
        AA_DEBUG("policy locked? %d\n", aa_g_lock_policy);
 
-       return policy_view_capable(ns) && capable && !aa_g_lock_policy;
+       return aa_policy_view_capable(label, ns) && capable &&
+               !aa_g_lock_policy;
+}
+
+bool aa_current_policy_view_capable(struct aa_ns *ns)
+{
+       struct aa_label *label;
+       bool res;
+
+       label = __begin_current_label_crit_section();
+       res = aa_policy_view_capable(label, ns);
+       __end_current_label_crit_section(label);
+
+       return res;
+}
+
+bool aa_current_policy_admin_capable(struct aa_ns *ns)
+{
+       struct aa_label *label;
+       bool res;
+
+       label = __begin_current_label_crit_section();
+       res = aa_policy_admin_capable(label, ns);
+       __end_current_label_crit_section(label);
+
+       return res;
 }
 
 /**
@@ -694,7 +734,7 @@ int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, u32 mask)
                return audit_policy(label, op, NULL, NULL, "policy_locked",
                                    -EACCES);
 
-       if (!policy_admin_capable(ns))
+       if (!aa_policy_admin_capable(label, ns))
                return audit_policy(label, op, NULL, NULL, "not policy admin",
                                    -EACCES);