From 238cf794ad382b10ff1e5ad12de4a9e2fd833e66 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 20 Jan 2022 11:04:01 -0600 Subject: [PATCH] cgroup-v1: Require capabilities to set release_agent ANBZ: #432 commit 12e1ce08da69be233d6827856c0f282da1023bb5 amazonlinux. The cgroup release_agent is called with call_usermodehelper. The function call_usermodehelper starts the release_agent with a full set fo capabilities. Therefore require capabilities when setting the release_agaent. Reported-by: Tabitha Sable Tested-by: Tabitha Sable Fixes: 81a6a5cdd2c5 ("Task Control Groups: automatic userspace notification of idle cgroups") Cc: stable@vger.kernel.org # v2.6.24+ Signed-off-by: "Eric W. Biederman" Signed-off-by: Tejun Heo [fllinden: modified for 4.14 for the mount options path, by looking up the right user namespace (like the fs context code) and passing it to parse_cgroupfs_options for a check] Signed-off-by: Frank van der Linden Signed-off-by: Tianchen Ding Acked-by: Michael Wang --- kernel/cgroup/cgroup-v1.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/kernel/cgroup/cgroup-v1.c b/kernel/cgroup/cgroup-v1.c index be5ce7d31678ec..413158a0df2dbd 100644 --- a/kernel/cgroup/cgroup-v1.c +++ b/kernel/cgroup/cgroup-v1.c @@ -579,6 +579,15 @@ static ssize_t cgroup_release_agent_write(struct kernfs_open_file *of, BUILD_BUG_ON(sizeof(cgrp->root->release_agent_path) < PATH_MAX); + /* + * Release agent gets called with all capabilities, + * require capabilities to set release agent. + */ + + if ((of->file->f_cred->user_ns != &init_user_ns) || + !capable(CAP_SYS_ADMIN)) + return -EPERM; + cgrp = cgroup_kn_lock_live(of->kn, false); if (!cgrp) return -ENODEV; @@ -1032,7 +1041,8 @@ static int cgroup1_show_options(struct seq_file *seq, struct kernfs_root *kf_roo return 0; } -static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts) +static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts, + struct user_namespace *user_ns) { char *token, *o = data; bool all_ss = false, one_ss = false; @@ -1084,6 +1094,13 @@ static int parse_cgroupfs_options(char *data, struct cgroup_sb_opts *opts) /* Specifying two release agents is forbidden */ if (opts->release_agent) return -EINVAL; + + /* + * Release agent gets called with all capabilities, + * require capabilities to set release agent. + */ + if ((user_ns != &init_user_ns) || !capable(CAP_SYS_ADMIN)) + return -EPERM; opts->release_agent = kstrndup(token + 14, PATH_MAX - 1, GFP_KERNEL); if (!opts->release_agent) @@ -1174,11 +1191,17 @@ static int cgroup1_remount(struct kernfs_root *kf_root, int *flags, char *data) struct cgroup_root *root = cgroup_root_from_kf(kf_root); struct cgroup_sb_opts opts; u16 added_mask, removed_mask; + struct user_namespace *user_ns; + struct super_block *sb; cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp); + sb = kernfs_pin_sb(kf_root, NULL); + user_ns = get_user_ns(sb->s_user_ns); /* See what subsystems are wanted */ - ret = parse_cgroupfs_options(data, &opts); + ret = parse_cgroupfs_options(data, &opts, user_ns); + put_user_ns(user_ns); + deactivate_super(sb); if (ret) goto out_unlock; @@ -1245,11 +1268,14 @@ struct dentry *cgroup1_mount(struct file_system_type *fs_type, int flags, struct dentry *dentry; int i, ret; bool new_root = false; + struct user_namespace *user_ns; cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp); + user_ns = get_user_ns(get_current_cred()->user_ns); /* First find the desired set of subsystems */ - ret = parse_cgroupfs_options(data, &opts); + ret = parse_cgroupfs_options(data, &opts, user_ns); + put_user_ns(user_ns); if (ret) goto out_unlock;