From a6ca2ed8912748242e3654dcd7ec89d6c942bb09 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 1 Feb 2019 10:57:49 +0100 Subject: [PATCH] cgroups: use of /sys/kernel/cgroup/delegate file This file contains the files one needs to chown to successfully delegate cgroup files to unprivileged users. Signed-off-by: Christian Brauner --- src/lxc/cgroups/cgfsng.c | 64 +++++++++++++++++++++++++++++++--------- src/lxc/cgroups/cgroup.c | 10 +++++-- src/lxc/cgroups/cgroup.h | 5 ++++ 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c index c4fc65252..d359b67fc 100644 --- a/src/lxc/cgroups/cgfsng.c +++ b/src/lxc/cgroups/cgfsng.c @@ -820,6 +820,7 @@ static struct hierarchy *add_hierarchy(struct hierarchy ***h, char **clist, char new->container_full_path = NULL; new->monitor_full_path = NULL; new->version = type; + new->cgroup2_chown = NULL; newentry = append_null_to_list((void ***)h); (*h)[newentry] = new; @@ -1640,13 +1641,11 @@ static int chown_cgroup_wrapper(void *data) if (arg->hierarchies[i]->version != CGROUP2_SUPER_MAGIC) continue; - fullpath = must_make_path(path, "cgroup.subtree_control", NULL); - (void)chowmod(fullpath, destuid, nsgid, 0664); - free(fullpath); - - fullpath = must_make_path(path, "cgroup.threads", NULL); - (void)chowmod(fullpath, destuid, nsgid, 0664); - free(fullpath); + for (char **p = arg->hierarchies[i]->cgroup2_chown; p && *p; p++) { + fullpath = must_make_path(path, *p, NULL); + (void)chowmod(fullpath, destuid, nsgid, 0664); + free(fullpath); + } } return 0; @@ -2524,10 +2523,40 @@ static bool cgroup_use_wants_controllers(const struct cgroup_ops *ops, return true; } +static void cg_unified_delegate(char ***delegate) +{ + char *tmp; + int idx; + char *standard[] = {"cgroup.subtree_control", "cgroup.threads", NULL}; + + tmp = read_file("/sys/kernel/cgroup/delegate"); + if (!tmp) { + for (char **p = standard; p && *p; p++) { + idx = append_null_to_list((void ***)delegate); + (*delegate)[idx] = must_copy_string(*p); + } + } else { + char *token; + lxc_iterate_parts (token, tmp, " \t\n") { + /* + * We always need to chown this for both cgroup and + * cgroup2. + */ + if (strcmp(token, "cgroup.procs") == 0) + continue; + + idx = append_null_to_list((void ***)delegate); + (*delegate)[idx] = must_copy_string(token); + } + free(tmp); + } +} + /* At startup, parse_hierarchies finds all the info we need about cgroup * mountpoints and current cgroups, and stores it in @d. */ -static bool cg_hybrid_init(struct cgroup_ops *ops, bool relative) +static bool cg_hybrid_init(struct cgroup_ops *ops, bool relative, + bool unprivileged) { int ret; char *basecginfo; @@ -2642,8 +2671,11 @@ static bool cg_hybrid_init(struct cgroup_ops *ops, bool relative) goto next; new = add_hierarchy(&ops->hierarchies, controller_list, mountpoint, base_cgroup, type); - if (type == CGROUP2_SUPER_MAGIC && !ops->unified) + if (type == CGROUP2_SUPER_MAGIC && !ops->unified) { + if (unprivileged) + cg_unified_delegate(&new->cgroup2_chown); ops->unified = new; + } continue; @@ -2719,11 +2751,13 @@ cleanup_on_err: return copy; } -static int cg_unified_init(struct cgroup_ops *ops, bool relative) +static int cg_unified_init(struct cgroup_ops *ops, bool relative, + bool unprivileged) { int ret; - char *mountpoint, *subtree_path; + char *mountpoint, *subtree_path, *tmp; char **delegatable; + struct hierarchy *new; char *base_cgroup = NULL; ret = cg_is_pure_unified(); @@ -2759,7 +2793,9 @@ static int cg_unified_init(struct cgroup_ops *ops, bool relative) * controllers per container. */ - add_hierarchy(&ops->hierarchies, delegatable, mountpoint, base_cgroup, CGROUP2_SUPER_MAGIC); + new = add_hierarchy(&ops->hierarchies, delegatable, mountpoint, base_cgroup, CGROUP2_SUPER_MAGIC); + if (!unprivileged) + cg_unified_delegate(&new->cgroup2_chown); ops->cgroup_layout = CGROUP_LAYOUT_UNIFIED; return CGROUP2_SUPER_MAGIC; @@ -2785,14 +2821,14 @@ static bool cg_init(struct cgroup_ops *ops, struct lxc_conf *conf) free(pin); } - ret = cg_unified_init(ops, relative); + ret = cg_unified_init(ops, relative, !lxc_list_empty(&conf->id_map)); if (ret < 0) return false; if (ret == CGROUP2_SUPER_MAGIC) return true; - return cg_hybrid_init(ops, relative); + return cg_hybrid_init(ops, relative, !lxc_list_empty(&conf->id_map)); } __cgfsng_ops static bool cgfsng_data_init(struct cgroup_ops *ops) diff --git a/src/lxc/cgroups/cgroup.c b/src/lxc/cgroups/cgroup.c index f0d4e1eff..bf92bf298 100644 --- a/src/lxc/cgroups/cgroup.c +++ b/src/lxc/cgroups/cgroup.c @@ -82,12 +82,16 @@ void cgroup_exit(struct cgroup_ops *ops) free(ops->container_cgroup); for (it = ops->hierarchies; it && *it; it++) { - char **ctrlr; + char **p; - for (ctrlr = (*it)->controllers; ctrlr && *ctrlr; ctrlr++) - free(*ctrlr); + for (p = (*it)->controllers; p && *p; p++) + free(*p); free((*it)->controllers); + for (p = (*it)->cgroup2_chown; p && *p; p++) + free(*p); + free((*it)->cgroup2_chown); + free((*it)->mountpoint); free((*it)->container_base_path); free((*it)->container_full_path); diff --git a/src/lxc/cgroups/cgroup.h b/src/lxc/cgroups/cgroup.h index d4dcd506b..b5d4c42bc 100644 --- a/src/lxc/cgroups/cgroup.h +++ b/src/lxc/cgroups/cgroup.h @@ -81,6 +81,11 @@ typedef enum { * CGROUP2_SUPER_MAGIC. */ struct hierarchy { + /* + * cgroup2 only: what files need to be chowned to delegate a cgroup to + * an unprivileged user. + */ + char **cgroup2_chown; char **controllers; char *mountpoint; char *container_base_path; -- 2.47.2