/* * May the requestor @r move victim @v to a new cgroup? * This is allowed if * . they are the same task * . they are ownedy by the same uid * . @r is root on the host, or * . @v's uid is mapped into @r's where @r is root. * * XXX do we want to add a restriction that @v must already * be under @r's cgroup? */ bool may_move_pid(pid_t r, uid_t r_uid, pid_t v) { uid_t v_uid, tmpuid; gid_t v_gid; if (r == v) return true; if (r_uid == 0) return true; get_pid_creds(v, &v_uid, &v_gid); if (r_uid == v_uid) return true; if (hostuid_to_ns(r_uid, r, &tmpuid) && tmpuid == 0 && hostuid_to_ns(v_uid, r, &tmpuid)) return true; return false; }
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; }
/* * pid may access path if the uids are the same, or if * path's uid is mapped into the userns and pid is root * there, or if the gids are the same and path has mode * in group rights, or if path has mode in other rights. * * uid and gid are passed in to avoid recomputation. uid * and gid are the host uids, not mapped into the ns. */ bool may_access(pid_t pid, uid_t uid, gid_t gid, const char *path, int mode) { struct stat sb; int ret; ret = stat(path, &sb); if (ret < 0) { nih_error("Could not look up %s\n", path); return false; } /* TODO - we should check capability set as well */ if (uid == 0) return true; if (uid == sb.st_uid) { if (mode == O_RDONLY && sb.st_mode & S_IRUSR) return true; if (mode == O_RDWR && ((sb.st_mode & (S_IRUSR|S_IWUSR)) == (S_IRUSR|S_IWUSR))) return true; if (mode == O_WRONLY && sb.st_mode & S_IWUSR) return true; } if (gid == sb.st_gid) { if (mode == O_RDONLY && sb.st_mode & S_IRGRP) return true; if (mode == O_RDWR && ((sb.st_mode & (S_IRGRP|S_IWGRP)) == (S_IRGRP|S_IWGRP))) return true; if (mode == O_WRONLY && sb.st_mode & S_IWGRP) return true; } if (hostuid_to_ns(uid, pid) == 0 && hostuid_to_ns(sb.st_uid, pid) != -1) return true; if (mode == O_RDONLY && sb.st_mode & S_IROTH) return true; if (mode == O_RDWR && ((sb.st_mode & (S_IROTH|S_IWOTH)) == (S_IROTH|S_IWOTH))) return true; if (mode == O_WRONLY && sb.st_mode & S_IWOTH) return true; return false; }