/* * Given a directory path, chown it to a userid. * We will chown $path and try to chown $path/tasks and $path/procs. * if @all_children is true, then chown all files under @path. (This * is for the case where the caller had the rights to mkdir the path. * In that case he gets to write to all files - the kernel will ensure * hierarhical limits) * * Return true so long as we could chown the directory itself. */ bool chown_cgroup_path(const char *path, uid_t uid, gid_t gid, bool all_children) { int len = strlen(path), ret; nih_local char *fpath = NULL; if (chown(path, uid, gid) < 0) return false; if (all_children) { struct dirent dirent, *direntp; char fpath[MAXPATHLEN]; DIR *d = opendir(path); if (len >= MAXPATHLEN) return true; strcpy(fpath, path); while (readdir_r(d, &dirent, &direntp) == 0 && direntp) { if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) continue; ret = snprintf(fpath+len, MAXPATHLEN-len, "/%s", direntp->d_name); if (ret < 0 || ret >= MAXPATHLEN-len) continue; if (chown(fpath, uid, gid) < 0) nih_info("Failed to chown file %s to %d:%d", fpath, (int)uid, (int)gid); } closedir(d); } else fpath = nih_sprintf(NULL, "%s/cgroup.procs", path); if (!fpath) return true; if (chown(fpath, uid, gid) < 0) nih_info("Failed to chown procs file %s", fpath); sprintf(fpath+len, "/tasks"); if (chown(fpath, uid, gid) < 0) nih_info("Failed to chown tasks file %s", fpath); return true; }
int get_value_main(void *parent, const char *controller, const char *cgroup, const char *key, struct ucred p, struct ucred r, char **value) { char path[MAXPATHLEN]; if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } if (!compute_pid_cgroup(r.pid, controller, cgroup, path, NULL)) { nih_error("%s: Could not determine the requested cgroup", __func__); return -1; } /* Check access rights to the cgroup directory */ if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) { nih_error("%s: Pid %d may not access %s\n", __func__, r.pid, path); return -1; } /* append the filename */ if (strlen(path) + strlen(key) + 2 > MAXPATHLEN) { nih_error("%s: filename too long for cgroup %s key %s", __func__, path, key); return -1; } strncat(path, "/", MAXPATHLEN-1); strncat(path, key, MAXPATHLEN-1); /* Check access rights to the file itself */ if (!may_access(r.pid, r.uid, r.gid, path, O_RDONLY)) { nih_error("%s: Pid %d may not access %s\n", __func__, r.pid, path); return -1; } /* read and return the value */ *value = file_read_string(parent, path); if (!*value) { nih_error("%s: Failed to read value from %s", __func__, path); return -1; } nih_info(_("Sending to client: %s"), *value); return 0; }
bool get_nih_io_creds(void *parent, NihIo *io, struct ucred *ucred) { NihIoMessage *msg = nih_io_read_message(parent, io); if (!msg) { nih_error("failed reading msg for ucred"); return false; } struct cmsghdr *cmsg = msg->control[0]; if (!cmsg) { nih_error("cmsg null"); return false; } if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_len != CMSG_LEN (sizeof(*ucred)) || cmsg->cmsg_type != SCM_CREDENTIALS) { nih_error("Got unexpected non-scm control message"); return false; } memcpy(ucred, CMSG_DATA(cmsg), sizeof(*ucred)); if (ucred->pid == -1) return false; nih_info(_("got creds pid %d (%u:%u)"), ucred->pid, ucred->uid, ucred->gid); return true; }
int remove_main(const char *controller, const char *cgroup, struct ucred p, struct ucred r, int recursive, int32_t *existed) { char rcgpath[MAXPATHLEN]; size_t cgroup_len; nih_local char *working = NULL, *copy = NULL, *wcgroup = NULL; char *p1; *existed = 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; } cgroup_len = strlen(cgroup); if (strlen(rcgpath) + cgroup_len > MAXPATHLEN) { nih_error("%s: Path name too long", __func__); return -1; } wcgroup = NIH_MUST( nih_strdup(NULL, cgroup) ); if (!normalize_path(wcgroup)) return -1; working = NIH_MUST( nih_strdup(NULL, rcgpath) ); NIH_MUST( nih_strcat(&working, NULL, "/") ); NIH_MUST( nih_strcat(&working, NULL, wcgroup) ); if (!dir_exists(working)) { *existed = -1; return 0; } // must have write access to the parent dir copy = NIH_MUST( nih_strdup(NULL, working) ); if (!(p1 = strrchr(copy, '/'))) return -1; *p1 = '\0'; if (!may_access(r.pid, r.uid, r.gid, copy, O_WRONLY)) { nih_error("%s: pid %d (%u:%u) may not remove %s", __func__, r.pid, r.uid, r.gid, copy); return -1; } if (!recursive) { if (rmdir(working) < 0) { nih_error("%s: Failed to remove %s: %s", __func__, working, strerror(errno)); return -1; } } else if (recursive_rmdir(working) < 0) return -1; nih_info(_("Removed %s for %d (%u:%u)"), working, r.pid, r.uid, r.gid); 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 do_move_pid_main(const char *controller, const char *cgroup, struct ucred p, struct ucred r, struct ucred v, bool escape) { char rcgpath[MAXPATHLEN], path[MAXPATHLEN]; FILE *f; pid_t query = r.pid; if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } // verify that ucred.pid may move target pid if (!may_move_pid(r.pid, r.uid, v.pid)) { nih_error("%s: %d may not move %d", __func__, r.pid, v.pid); return -1; } // Get r's current cgroup in rcgpath if (escape) query = p.pid; if (!compute_pid_cgroup(query, controller, "", rcgpath, NULL)) { nih_error("%s: Could not determine the requested cgroup", __func__); return -1; } // If the victim is not under proxy's cgroup, refuse if (!victim_under_proxy_cgroup(rcgpath, v.pid, controller)) { nih_error("%s: victim's cgroup is not under proxy's (p.uid %u)", __func__, p.uid); return -1; } /* rcgpath + / + cgroup + /tasks + \0 */ if (strlen(rcgpath) + strlen(cgroup) > MAXPATHLEN - 8) { nih_error("%s: Path name too long", __func__); return -1; } strcpy(path, rcgpath); strncat(path, "/", MAXPATHLEN-1); strncat(path, cgroup, MAXPATHLEN-1); 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; } // is r allowed to write to tasks file? strncat(path, "/tasks", MAXPATHLEN-1); if (!may_access(r.pid, r.uid, r.gid, path, O_WRONLY)) { nih_error("%s: pid %d (uid %u gid %u) may not write to %s", __func__, r.pid, r.uid, r.gid, path); return -1; } f = fopen(path, "w"); if (!f) { nih_error("%s: Failed to open %s", __func__, path); return -1; } if (fprintf(f, "%d\n", v.pid) < 0) { fclose(f); nih_error("%s: Failed to write %d to %s", __func__, v.pid, path); return -1; } if (fclose(f) != 0) { nih_error("%s: Failed to write %d to %s", __func__, v.pid, path); return -1; } nih_info(_("%d moved to %s:%s by %d's request"), v.pid, controller, cgroup, r.pid); return 0; }
/** * Mount the cgroup filesystems and record the information. * This should take configuration data from /etc. For now, * Just mount all controllers, separately just as cgroup-lite * does, and set the use_hierarchy and clone_children options. * * Things which should go into configuration file: * . which controllers to mount * . which controllers to co-mount * . any mount options (per-controller) * . values for sane_behavior, use_hierarchy, and clone_children */ int setup_cgroup_mounts(void) { FILE *cgf; int ret; char line[400]; if (unshare(CLONE_NEWNS) < 0) { nih_fatal("Failed to unshare a private mount ns: %s", strerror(errno)); return -1; } if (!setup_base_path()) { nih_fatal("Error setting up base cgroup path"); return -1; } if ((cgf = fopen("/proc/cgroups", "r")) == NULL) { nih_fatal ("Error opening /proc/cgroups: %s", strerror(errno)); return -1; } while (fgets(line, 400, cgf)) { char *p; struct controller_mounts *tmp; char dest[MAXPATHLEN]; unsigned long h; if (line[0] == '#') continue; p = index(line, '\t'); if (!p) continue; *p = '\0'; h = strtoul(p+1, NULL, 10); if (h) { nih_info("%s was already mounted!", line); #if STRICT ret = -1; goto out; #endif } ret = snprintf(dest, MAXPATHLEN, "%s/%s", base_path, line); if (ret < 0 || ret >= MAXPATHLEN) { nih_fatal("Error calculating pathname for %s and %s", base_path, line); goto out; } if (mkdir(dest, 0755) < 0 && errno != EEXIST) { nih_fatal("Failed to create %s: %s", dest, strerror(errno)); ret = -1; goto out; } if ((ret = mount(line, dest, "cgroup", 0, line)) < 0) { nih_fatal("Failed mounting %s: %s", line, strerror(errno)); goto out; } ret = -1; tmp = realloc(all_mounts, (num_controllers+1) * sizeof(*all_mounts)); if (!tmp) { nih_fatal("Out of memory mounting controllers"); goto out; } all_mounts = tmp; all_mounts[num_controllers].controller = strdup(line); if (!all_mounts[num_controllers].controller) { nih_fatal("Out of memory mounting controllers"); goto out; } all_mounts[num_controllers].options = NULL; all_mounts[num_controllers].path = strdup(dest); if (!all_mounts[num_controllers].path) { nih_fatal("Out of memory mounting controllers"); goto out; } nih_info("Mounted %s onto %s", all_mounts[num_controllers].controller, all_mounts[num_controllers].path); if (strcmp(all_mounts[num_controllers].controller, "cpuset") == 0) { set_clone_children(dest); // TODO make this optional? nih_info("set clone_children"); } else if (strcmp(all_mounts[num_controllers].controller, "memory") == 0) { set_use_hierarchy(dest); // TODO make this optional? nih_info("set memory.use_hierarchy"); } num_controllers++; } nih_info("mounted %d controllers", num_controllers); ret = 0; out: fclose(cgf); return ret; }