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 follow 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 aesteve@redhat.com --- security/selinux/hooks.c | 33 +++++++++++++++++++++++++++++++++ security/selinux/include/classmap.h | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 8f39d91eabc9..8de5e16951de 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2210,6 +2210,38 @@ static int selinux_dma_buf_charge_task(const struct cred *from, const struct cre SECCLASS_DMA_BUF, DMA_BUF__CHARGE_TASK, NULL); }
+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, clen, &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); +} + static int selinux_quotactl(int cmds, int type, int id, const struct super_block *sb) { const struct cred *cred = current_cred(); @@ -7681,6 +7713,7 @@ 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), + LSM_HOOK_INIT(dma_buf_charge_cgroup, selinux_dma_buf_charge_cgroup), 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, {} } };