int chown_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) { _cleanup_free_ char *path = NULL, *fs = NULL; int r; r = cg_pid_get_path(NULL, pid, &path); if (r < 0) return log_error_errno(r, "Failed to get container cgroup path: %m"); r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs); if (r < 0) return log_error_errno(r, "Failed to get file system path for container cgroup: %m"); r = chown_cgroup_path(fs, uid_shift); if (r < 0) return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs); if (unified_requested == CGROUP_UNIFIED_SYSTEMD || (unified_requested == CGROUP_UNIFIED_NONE && cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0)) { _cleanup_free_ char *lfs = NULL; /* Always propagate access rights from unified to legacy controller */ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, NULL, &lfs); if (r < 0) return log_error_errno(r, "Failed to get file system path for container cgroup: %m"); r = chown_cgroup_path(lfs, uid_shift); if (r < 0) return log_error_errno(r, "Failed to chown() cgroup %s: %m", lfs); } return 0; }
int chown_main(const char *controller, const char *cgroup, struct ucred p, struct ucred r, struct ucred v) { char rcgpath[MAXPATHLEN]; nih_local char *path = NULL; uid_t uid; /* If caller is not root in his userns, then he can't chown, as * that requires privilege over two uids */ if (r.uid) { if (!hostuid_to_ns(r.uid, r.pid, &uid) || uid != 0) { nih_error("%s: Chown requested by non-root uid %u", __func__, r.uid); return -1; } } if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } // Get r's current cgroup in rcgpath if (!compute_pid_cgroup(r.pid, controller, "", rcgpath, NULL)) { nih_error("%s: Could not determine the requested cgroup", __func__); return -1; } /* rcgpath + / + cgroup + \0 */ if (strlen(rcgpath) + strlen(cgroup) > MAXPATHLEN+2) { nih_error("%s: Path name too long", __func__); return -1; } path = NIH_MUST( nih_sprintf(NULL, "%s/%s", rcgpath, cgroup) ); if (realpath_escapes(path, rcgpath)) { nih_error("%s: Invalid path %s", __func__, path); return -1; } // is r allowed to descend under the parent dir? if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) { nih_error("%s: pid %d (uid %u gid %u) may not read under %s", __func__, r.pid, r.uid, r.gid, path); return -1; } // does r have privilege over the cgroup dir? if (!may_access(r.pid, r.uid, r.gid, path, O_RDWR)) { nih_error("%s: Pid %d may not chown %s\n", __func__, r.pid, path); return -1; } // go ahead and chown it. if (!chown_cgroup_path(path, v.uid, v.gid, false)) { nih_error("%s: Failed to change ownership on %s to %u:%u", __func__, path, v.uid, v.gid); return -1; } return 0; }
int chown_cgroup(pid_t pid, uid_t uid_shift) { _cleanup_free_ char *path = NULL, *fs = NULL; int r; r = cg_pid_get_path(NULL, pid, &path); if (r < 0) return log_error_errno(r, "Failed to get container cgroup path: %m"); r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs); if (r < 0) return log_error_errno(r, "Failed to get file system path for container cgroup: %m"); r = chown_cgroup_path(fs, uid_shift); if (r < 0) return log_error_errno(r, "Failed to chown() cgroup %s: %m", fs); return 0; }
int create_main(const char *controller, const char *cgroup, struct ucred p, struct ucred r, int32_t *existed) { int ret, depth; char rcgpath[MAXPATHLEN], path[MAXPATHLEN], dirpath[MAXPATHLEN]; nih_local char *copy = NULL; size_t cgroup_len; char *p1, *p2, oldp2; *existed = 1; if (!cgroup || ! *cgroup) // nothing to do return 0; if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } // Get r's current cgroup in rcgpath if (!compute_pid_cgroup(r.pid, controller, "", rcgpath, &depth)) { nih_error("%s: Could not determine the requested cgroup", __func__); return -1; } if (depth > maxdepth) { nih_error("%s: Cgroup too deep: %s/%s", __func__, rcgpath, cgroup); return -1; } cgroup_len = strlen(cgroup); if (strlen(rcgpath) + cgroup_len > MAXPATHLEN) { nih_error("%s: Path name too long", __func__); return -1; } copy = NIH_MUST( nih_strndup(NULL, cgroup, cgroup_len) ); strcpy(path, rcgpath); strcpy(dirpath, rcgpath); for (p1=copy; *p1; p1 = p2) { *existed = -1; for (p2=p1; *p2 && *p2 != '/'; p2++); oldp2 = *p2; *p2 = '\0'; if (strcmp(p1, "..") == 0) { nih_error("%s: Invalid cgroup path at create: %s", __func__, p1); return -1; } strncat(path, "/", MAXPATHLEN-1); strncat(path, p1, MAXPATHLEN-1); if (dir_exists(path)) { *existed = 1; // TODO - properly use execute perms if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) { nih_error("%s: pid %d (uid %u gid %u) may not look under %s", __func__, r.pid, r.uid, r.gid, path); return -1; } goto next; } if (!may_access(r.pid, r.uid, r.gid, dirpath, O_RDWR)) { nih_error("%s: pid %d (uid %u gid %u) may not create under %s", __func__, r.pid, r.uid, r.gid, dirpath); return -1; } ret = mkdir(path, 0755); if (ret < 0) { // Should we ignore EEXIST? Ok, but don't chown. if (errno == EEXIST) { *existed = 1; goto next; } nih_error("%s: failed to create %s", __func__, path); return -1; } if (!chown_cgroup_path(path, r.uid, r.gid, true)) { nih_error("%s: Failed to change ownership on %s to %u:%u", __func__, path, r.uid, r.gid); rmdir(path); return -1; } *existed = -1; next: strncat(dirpath, "/", MAXPATHLEN-1); strncat(dirpath, p1, MAXPATHLEN-1); *p2 = oldp2; if (*p2) p2++; } nih_info(_("Created %s for %d (%u:%u)"), path, r.pid, r.uid, r.gid); return 0; }
int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t uid_shift) { _cleanup_free_ char *cgroup = NULL; char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1]; bool undo_mount = false; const char *fn; int r, unified_controller; unified_controller = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); if (unified_controller < 0) return log_error_errno(unified_controller, "Failed to determine whether the systemd hierarchy is unified: %m"); if ((unified_controller > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD)) return 0; /* When the host uses the legacy cgroup setup, but the * container shall use the unified hierarchy, let's make sure * we copy the path from the name=systemd hierarchy into the * unified hierarchy. Similar for the reverse situation. */ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); if (r < 0) return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid); /* In order to access the unified hierarchy we need to mount it */ if (!mkdtemp(tree)) return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m"); if (unified_controller > 0) r = mount_verbose(LOG_ERR, "cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr"); else r = mount_verbose(LOG_ERR, "cgroup", tree, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL); if (r < 0) goto finish; undo_mount = true; /* If nspawn dies abruptly the cgroup hierarchy created below * its unit isn't cleaned up. So, let's remove it * https://github.com/systemd/systemd/pull/4223#issuecomment-252519810 */ fn = strjoina(tree, cgroup); (void) rm_rf(fn, REMOVE_ROOT|REMOVE_ONLY_DIRECTORIES); fn = strjoina(tree, cgroup, "/cgroup.procs"); (void) mkdir_parents(fn, 0755); sprintf(pid_string, PID_FMT, pid); r = write_string_file(fn, pid_string, 0); if (r < 0) { log_error_errno(r, "Failed to move process: %m"); goto finish; } fn = strjoina(tree, cgroup); r = chown_cgroup_path(fn, uid_shift); if (r < 0) log_error_errno(r, "Failed to chown() cgroup %s: %m", fn); finish: if (undo_mount) (void) umount_verbose(tree); (void) rmdir(tree); return r; }