The security_dma_buf_charge_cgroup() hook allows security modules
to control which processes may charge dma-buf to an arbitrary
cgroup via cgroupfd passed in charge_fd. Without a policy
implementation, the hook is a no-op and any process that can open a
cgroup directory (effectively any process) can charge memory to it.

Implement selinux_dma_buf_charge_cgroup() using avc_has_perm() with
the dma_buf object class and a charge_cgroup permission. The target
SID is derived from the SELinux label on the cgroup's kernfs
directory, so access control follows the cgroup filesystem labeling.
Policy authors can then grant cross-cgroup charging selectively,
for example:

  allow allocator_app_t cgroup_t:dma_buf charge_cgroup;

The hook matches the fail-open semantics of
selinux_kernfs_init_security(). This means unlabeled cgroups are
unrestricted by default.

Signed-off-by: Albert Esteve <[email protected]>
---
 security/selinux/hooks.c            | 37 +++++++++++++++++++++++++++++++++++++
 security/selinux/include/classmap.h |  2 +-
 2 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 8f39d91eabc9..2f99eaa910af 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2210,6 +2210,40 @@ static int selinux_dma_buf_charge_task(const struct cred 
*from, const struct cre
                            SECCLASS_DMA_BUF, DMA_BUF__CHARGE_TASK, NULL);
 }
 
+#ifdef CONFIG_CGROUPS
+static int selinux_dma_buf_charge_cgroup(const struct cred *cred, struct 
cgroup *cgrp)
+{
+       u32 cgrp_sid, clen;
+       int rc;
+       char *context;
+
+       rc = kernfs_xattr_get(cgrp->kn, XATTR_NAME_SELINUX, NULL, 0);
+       if (rc == -ENODATA)
+               return 0;
+       else if (rc < 0)
+               return rc;
+
+       clen = (u32)rc;
+       context = kmalloc(clen, GFP_KERNEL);
+       if (!context)
+               return -ENOMEM;
+
+       rc = kernfs_xattr_get(cgrp->kn, XATTR_NAME_SELINUX, context, clen);
+       if (rc < 0) {
+               kfree(context);
+               return rc;
+       }
+
+       rc = security_context_to_sid(context, rc, &cgrp_sid, GFP_KERNEL);
+       kfree(context);
+       if (rc)
+               return rc;
+
+       return avc_has_perm(cred_sid(cred), cgrp_sid,
+                           SECCLASS_DMA_BUF, DMA_BUF__CHARGE_CGROUP, NULL);
+}
+#endif
+
 static int selinux_quotactl(int cmds, int type, int id, const struct 
super_block *sb)
 {
        const struct cred *cred = current_cred();
@@ -7681,6 +7715,9 @@ static struct security_hook_list selinux_hooks[] 
__ro_after_init = {
        LSM_HOOK_INIT(capset, selinux_capset),
        LSM_HOOK_INIT(capable, selinux_capable),
        LSM_HOOK_INIT(dma_buf_charge_task, selinux_dma_buf_charge_task),
+#ifdef CONFIG_CGROUPS
+       LSM_HOOK_INIT(dma_buf_charge_cgroup, selinux_dma_buf_charge_cgroup),
+#endif
        LSM_HOOK_INIT(quotactl, selinux_quotactl),
        LSM_HOOK_INIT(quota_on, selinux_quota_on),
        LSM_HOOK_INIT(syslog, selinux_syslog),
diff --git a/security/selinux/include/classmap.h 
b/security/selinux/include/classmap.h
index b4ebedc6e424..90d1e04c3d27 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -181,7 +181,7 @@ const struct security_class_mapping secclass_map[] = {
        { "user_namespace", { "create", NULL } },
        { "memfd_file",
          { COMMON_FILE_PERMS, "execute_no_trans", "entrypoint", NULL } },
-       { "dma_buf", { "charge_task", NULL } },
+       { "dma_buf", { "charge_task", "charge_cgroup", NULL } },
        /* last one */ { NULL, {} }
 };
 

-- 
2.55.0

Reply via email to