bool send_dummy_msg(DBusConnection *conn) { DBusMessage *message = NULL; DBusMessageIter iter; int a; message = dbus_message_new_method_call(dbus_bus_get_unique_name(conn), "/org/linuxcontainers/cgmanager", "org.linuxcontainers.cgmanager0_0", "Ping"); if (!message) { nih_error("%s: failed to create ping message", __func__); return false; } dbus_message_set_no_reply(message, TRUE); dbus_message_iter_init_append(message, &iter); if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &a)) { dbus_message_unref(message); nih_error("%s: out of memory", __func__); return false; } dbus_connection_send(conn, message, NULL); dbus_connection_flush(conn); dbus_message_unref(message); return true; }
int get_tasks_main(void *parent, const char *controller, const char *cgroup, struct ucred p, struct ucred r, int32_t **pids) { char path[MAXPATHLEN]; const char *key = "tasks"; 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); return file_read_pids(parent, path, pids); }
/* * We may decide to make the socket path customizable. For now * just assume it is in /sys/fs/cgroup/ which has some special * consequences */ static bool setup_cgroup_dir(void) { int ret; if (!dir_exists(CGDIR)) { nih_debug(CGDIR " does not exist"); return false; } if (daemon_running()) { nih_error("%s: cgmanager is already running", __func__); return false; } if (file_exists(CGMANAGER_SOCK)) { if (unlink(CGMANAGER_SOCK) < 0) { nih_error("%s: failed to delete stale cgmanager socket", __func__); return false; } } /* Check that /sys/fs/cgroup is writeable, else mount a tmpfs */ unlink(CGPROBE); ret = creat(CGPROBE, O_RDWR); if (ret >= 0) { close(ret); unlink(CGPROBE); return mkdir_cgmanager_dir(); } ret = mount("cgroup", CGDIR, "tmpfs", 0, "size=10000"); if (ret) { nih_debug("Failed to mount tmpfs on %s: %s", CGDIR, strerror(errno)); return false; } nih_debug("Mounted tmpfs onto %s", CGDIR); return mkdir_cgmanager_dir(); }
/* GetPidCgroupAbs */ int get_pid_cgroup_abs_main(void *parent, const char *controller,struct ucred p, struct ucred r, struct ucred v, char **output) { char rcgpath[MAXPATHLEN], vcgpath[MAXPATHLEN]; // Get p's current cgroup in rcgpath if (!compute_pid_cgroup(p.pid, controller, "", rcgpath, NULL)) { nih_error("%s: Could not determine the requestor cgroup", __func__); return -1; } // Get v's cgroup in vcgpath if (!compute_pid_cgroup(v.pid, controller, "", vcgpath, NULL)) { nih_error("%s: Could not determine the victim cgroup", __func__); return -1; } // Make sure v's cgroup is under p's int rlen = strlen(rcgpath); if (strncmp(rcgpath, vcgpath, rlen) != 0) { nih_error("%s: v (%d)'s cgroup is not below p (%d)'s", __func__, v.pid, p.pid); return -1; } if (strlen(vcgpath) == rlen) *output = NIH_MUST (nih_strdup(parent, "/") ); else *output = NIH_MUST (nih_strdup(parent, vcgpath + rlen) ); return 0; }
/* * get_pid_creds: get the real uid and gid of @pid from * /proc/$$/status * (XXX should we use euid here?) */ void get_pid_creds(pid_t pid, uid_t *uid, gid_t *gid) { char line[400]; int u, g; FILE *f; *uid = -1; *gid = -1; sprintf(line, "/proc/%d/status", (int)pid); if ((f = fopen(line, "r")) == NULL) { nih_error("Error opening %s: %s", line, strerror(errno)); return; } while (fgets(line, 400, f)) { if (strncmp(line, "Uid:", 4) == 0) { if (sscanf(line+4, "%d", &u) != 1) { nih_error("bad uid line for pid %d", (int)pid); fclose(f); return; } *uid = (uid_t)u; } else if (strncmp(line, "Gid:", 4) == 0) { if (sscanf(line+4, "%d", &g) != 1) { nih_error("bad gid line for pid %d", (int)pid); fclose(f); return; } *gid = (uid_t)g; } } fclose(f); }
static DBusMessage *start_dbus_request(const char *method, int *sv) { int optval = 1; DBusMessage *msg = NULL; if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) { nih_error("%s: Error creating socketpair: %s", __func__, strerror(errno)); return NULL; } if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { nih_error("%s: setsockopt: %s", __func__, strerror(errno)); goto err; } if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { nih_error("%s: setsockopt: %s", __func__, strerror(errno)); goto err; } msg = dbus_message_new_method_call(dbus_bus_get_unique_name(server_conn), "/org/linuxcontainers/cgmanager", "org.linuxcontainers.cgmanager0_0", method); if (msg) return msg; err: close(sv[0]); close(sv[1]); return NULL; }
int remove_on_empty_main(const char *controller, const char *cgroup, struct ucred p, struct ucred r) { char rcgpath[MAXPATHLEN]; size_t cgroup_len; nih_local char *working = NULL, *wcgroup = NULL; 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)) { return -1; } // must have write access if (!may_access(r.pid, r.uid, r.gid, working, O_WRONLY)) { nih_error("%s: pid %d (%u:%u) may not remove %s", __func__, r.pid, r.uid, r.gid, working); return -1; } NIH_MUST( nih_strcat(&working, NULL, "/notify_on_release") ); if (!set_value_trusted(working, "1\n")) { nih_error("Failed to set remove_on_empty for %s:%s", controller, working); return -1; } return 0; }
/* * Get a pid passed in a SCM_CREDENTIAL over a unix socket * @sock: the socket fd. * Credentials are invalid of *p == 1. * Note - this is a synchronous version. We use it only in the proxy to wait * on the server, since there is no sense not hanging in that case. */ void get_scm_creds_sync(int sock, struct ucred *cred) { struct msghdr msg = { 0 }; struct iovec iov; struct cmsghdr *cmsg; char cmsgbuf[CMSG_SPACE(sizeof(*cred))]; char buf[1]; int ret; int optval = 1; cred->pid = -1; cred->uid = -1; cred->gid = -1; if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) { nih_error("Failed to set passcred: %s", strerror(errno)); return; } buf[0] = '1'; if (write(sock, buf, 1) != 1) { nih_error("Failed to start write on scm fd: %s", strerror(errno)); return; } msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; // retry logic is not ideal, especially as we are not // threaded. Sleep at most 1 second waiting for the client // to send us the scm_cred ret = recvmsg(sock, &msg, 0); if (ret < 0) { nih_error("Failed to receive scm_cred: %s", strerror(errno)); return; } cmsg = CMSG_FIRSTHDR(&msg); if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { memcpy(cred, CMSG_DATA(cmsg), sizeof(*cred)); } }
int move_pid_main (const char *controller, const char *cgroup, struct ucred p, struct ucred r, struct ucred v) { if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } if (cgroup[0] == '/') { nih_error("%s: uid %u tried to escape its cgroup", __func__, r.uid); return -1; } return do_move_pid_main(controller, cgroup, p, r, v, "MovePidScm"); }
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 remove_main (const char *controller, const char *cgroup, struct ucred p, struct ucred r, int recursive, int32_t *existed) { DBusMessage *message; DBusMessageIter iter; int sv[2], ret = -1; char buf[1]; if (memcmp(&p, &r, sizeof(struct ucred)) != 0) { nih_error("%s: proxy != requestor", __func__); return -1; } if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } if (!(message = start_dbus_request("RemoveScm", sv))) { nih_error("%s: error starting dbus request", __func__); return -1; } dbus_message_iter_init_append(message, &iter); if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &controller)) { nih_error("%s: out of memory", __func__); dbus_message_unref(message); goto out; } if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &cgroup)) { nih_error("%s: out of memory", __func__); dbus_message_unref(message); goto out; } if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &recursive)) { nih_error("%s: out of memory", __func__); dbus_message_unref(message); goto out; } if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_UNIX_FD, &sv[1])) { nih_error("%s: out of memory", __func__); dbus_message_unref(message); goto out; } if (!complete_dbus_request(message, sv, &r, NULL)) { nih_error("%s: error completing dbus request", __func__); goto out; } if (proxyrecv(sv[0], buf, 1) == 1 && (*buf == '1' || *buf == '2')) ret = 0; *existed = *buf == '2' ? 1 : -1; out: close(sv[0]); close(sv[1]); return ret; }
char *file_read_string(void *parent, const char *path) { int ret, fd = open(path, O_RDONLY); char *string = NULL; off_t sz = 0; if (fd < 0) { nih_error("Error opening %s: %s", path, strerror(errno)); return NULL; } while (1) { char *n; sz += 1024; if (!(n = nih_realloc(string, parent, sz))) { nih_free(string); string = NULL; goto out; } string = n; memset(string+sz-1024, 0, 1024); ret = read(fd, string+sz-1024, 1024); if (ret < 0) { nih_free(string); string = NULL; goto out; } if (ret < 1024) break; } out: close(fd); drop_newlines(string); return string; }
int send_creds(int sock, struct ucred *cred) { struct msghdr msg = { 0 }; struct iovec iov; struct cmsghdr *cmsg; char cmsgbuf[CMSG_SPACE(sizeof(*cred))]; char buf[1]; buf[0] = 'p'; msg.msg_control = cmsgbuf; msg.msg_controllen = sizeof(cmsgbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; memcpy(CMSG_DATA(cmsg), cred, sizeof(*cred)); msg.msg_name = NULL; msg.msg_namelen = 0; iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_iov = &iov; msg.msg_iovlen = 1; if (sendmsg(sock, &msg, 0) < 0) { nih_error("%s: failed at sendmsg: %s", __func__, strerror(errno)); return -1; } return 0; }
static inline int mkdir_cgmanager_dir(void) { if (mkdir(CGMANAGER_DIR, 0755) == -1 && errno != EEXIST) { nih_error("%s: Could not create %s", __func__, CGMANAGER_DIR); return false; } return true; }
static void cgm_dbus_disconnected(DBusConnection *connection) { NihError *err; dbus_connection_unref(connection); while (1) { server_conn = nih_dbus_connect(CGPROXY_DBUS_PATH, cgm_dbus_disconnected); if (server_conn) return; err = nih_error_get(); nih_error("Failed to re-open connection to %s: %s", CGPROXY_DBUS_PATH, err->message); nih_free(err); nih_error("re-trying in 5 seconds\n"); sleep(5); } }
/** * cancel_callback: * @data: not used, * @signal: signal caught. * * This callback is run whenever one of the "cancel running shutdown" * signals is sent to us. * * This does not return. **/ static void cancel_callback (void *data, NihSignal *signal) { nih_error (_("Shutdown cancelled")); unlink (ETC_NOLOGIN); nih_main_unlink_pidfile (); exit (0); }
/* * Calculate a full path to the cgroup being requested. * @pid is the process making the request * @controller is the mounted controller under which we will look. * @cgroup is the cgroup which @pid is asking about. If @cgroup is * @path is the path in which to return the full cgroup path. * "a/b", then we concatenate "/cgroup/for/pid" with "a/b" * If @cgroup is "/a/b", then we use "/a/b" */ bool compute_pid_cgroup(pid_t pid, const char *controller, const char *cgroup, char *path) { int ret; char requestor_cgpath[MAXPATHLEN], fullpath[MAXPATHLEN], *cg; const char *cont_path; bool abspath = false; if (cgroup && cgroup[0] != '/') { cg = pid_cgroup(pid, controller, requestor_cgpath); if (!cg) { return false; } } else abspath = true; if ((cont_path = get_controller_path(controller)) == NULL) { nih_error("Controller %s not mounted", controller); return false; } /* append the requested cgroup */ ret = snprintf(fullpath, MAXPATHLEN, "%s/%s%s%s", cont_path, abspath ? "" : cg, abspath ? "" : "/", cgroup ? cgroup : ""); if (ret < 0 || ret >= MAXPATHLEN) { nih_error("Path name too long: %s/%s/%s", cont_path, cg, cgroup); return false; } /* Make sure client isn't passing us a bunch of bogus '../'s to * try to read host files */ if (!realpath(fullpath, path)) { nih_error("Invalid path %s", fullpath); return false; } if (strncmp(path, cont_path, strlen(cont_path)) != 0) { nih_error("invalid cgroup path '%s' for pid %d", cgroup, (int)pid); return false; } return true; }
int move_pid_main(const char *controller, const char *cgroup, struct ucred p, struct ucred r, struct ucred v) { if (cgroup[0] == '/') { // We could try to be accomodating, but let's not fool around right now nih_error("%s: Bad requested cgroup path: %s", __func__, cgroup); return -1; } return do_move_pid_main(controller, cgroup, p, r, v, false); }
static bool complete_dbus_request(DBusMessage *message, int *sv, struct ucred *rcred, struct ucred *vcred) { char buf[1]; if (!dbus_connection_send(server_conn, message, NULL)) { nih_error("%s: failed to send dbus message", __func__); dbus_message_unref(message); return false; } dbus_connection_flush(server_conn); dbus_message_unref(message); if (proxyrecv(sv[0], buf, 1) != 1) { nih_error("%s: Error getting reply from server over socketpair", __func__); return false; } if (send_creds(sv[0], rcred)) { nih_error("%s: Error sending pid over SCM_CREDENTIAL", __func__); return false; } if (!vcred) // this request only requires one scm_credential return true; if (proxyrecv(sv[0], buf, 1) != 1) { nih_error("%s: Error getting reply from server over socketpair", __func__); return false; } if (send_creds(sv[0], vcred)) { nih_error("%s: Error sending pid over SCM_CREDENTIAL", __func__); return false; } return true; }
static bool victim_under_proxy_cgroup(char *rcgpath, pid_t v, const char *controller) { char vcgpath[MAXPATHLEN]; if (!compute_pid_cgroup(v, controller, "", vcgpath, NULL)) { nih_error("%s: Could not determine the victim's cgroup", __func__); return false; } if (strncmp(vcgpath, rcgpath, strlen(rcgpath)) != 0) return false; return true; }
/* * pid_cgroup: return the cgroup of @pid for @controller. * retv must be a (at least) MAXPATHLEN size buffer into * which the answer will be copied. */ static inline char *pid_cgroup(pid_t pid, const char *controller, char *retv) { FILE *f; char path[100]; char *line = NULL, *cgroup = NULL; size_t len = 0; sprintf(path, "/proc/%d/cgroup", (int) pid); if ((f = fopen(path, "r")) == NULL) { nih_error("could not open cgroup file for %d", (int) pid); return NULL; } while (getline(&line, &len, f) != -1) { char *c1, *c2; char *token, *saveptr = NULL; if ((c1 = index(line, ':')) == NULL) continue; if ((c2 = index(++c1, ':')) == NULL) continue; *c2 = '\0'; for (; (token = strtok_r(c1, ",", &saveptr)); c1 = NULL) { if (strcmp(token, controller) != 0) continue; if (strlen(c2+1) + 1 > MAXPATHLEN) { nih_error("cgroup name too long"); goto found; } strncpy(retv, c2+1, strlen(c2+1)+1); drop_newlines(retv); cgroup = retv; goto found; } } found: fclose(f); free(line); return cgroup; }
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 move_pid_abs_main (const char *controller, const char *cgroup, struct ucred p, struct ucred r, struct ucred v) { #if 0 /* * We used to enforce that r must be root. However that's * overly restrictive. * Cgmanager ensures that r must have write access to the * tasks file. That seems sufficient. However if it is deemed * insufficient, we can ensure that r's user or group id own * all parent directories up to a common parent, from v.cgroup * to the requested cgroup. THIS CODE does NOT do that. */ if (r.uid) { nih_error("%s: uid %u tried to escape", __func__, r.uid); return -1; } #endif if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } return do_move_pid_main(controller, cgroup, p, r, v, "MovePidAbsScm"); }
int list_children_main(void *parent, const char *controller, const char *cgroup, struct ucred p, struct ucred r, char ***output) { char path[MAXPATHLEN]; *output = NULL; 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; } return get_child_directories(parent, path, output); }
/* * Recursively delete a cgroup. * Cgroup files can't be deleted, but are cleaned up when you remove the * containing directory. A directory cannot be removed until all its * children are removed, and can't be removed if any tasks remain. * * We allow any task which may write under /a/b to delete any cgroups * under that, even if, say, it technically is not allowed to remove * /a/b/c/d/. */ static int recursive_rmdir(char *path) { struct dirent dirent, *direntp; DIR *dir; char pathname[MAXPATHLEN]; int failed = 0; dir = opendir(path); if (!dir) { nih_error("%s: Failed to open dir %s for recursive deletion", __func__, path); return -1; } while (!readdir_r(dir, &dirent, &direntp)) { struct stat mystat; int rc; if (!direntp) break; if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, "..")) continue; rc = snprintf(pathname, MAXPATHLEN, "%s/%s", path, direntp->d_name); if (rc < 0 || rc >= MAXPATHLEN) { failed = 1; continue; } rc = lstat(pathname, &mystat); if (rc) { failed = 1; continue; } if (S_ISDIR(mystat.st_mode)) { if (recursive_rmdir(pathname) < 0) failed = 1; } } if (closedir(dir) < 0) failed = 1; if (rmdir(path) < 0) failed = 1; return failed ? -1 : 0; }
int chmod_main(const char *controller, const char *cgroup, const char *file, struct ucred p, struct ucred r, int mode) { char rcgpath[MAXPATHLEN]; nih_local char *path = NULL; if (!sane_cgroup(cgroup)) { nih_error("%s: unsafe cgroup", __func__); return -1; } if (file && ( strchr(file, '/') || strchr(file, '\\')) ) { nih_dbus_error_raise_printf (DBUS_ERROR_INVALID_ARGS, "invalid file"); 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; } path = NIH_MUST( nih_sprintf(NULL, "%s/%s", rcgpath, cgroup) ); if (file && strlen(file)) NIH_MUST( nih_strcat_sprintf(&path, NULL, "/%s", file) ); 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 chmod %s\n", __func__, r.pid, path); return -1; } // go ahead and chmod it. if (!chmod_cgroup_path(path, mode)) { nih_error("%s: Failed to change mode on %s to %d", __func__, path, mode); 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; }
int get_pid_cgroup_abs_main (void *parent, char *controller, struct ucred p, struct ucred r, struct ucred v, char **output) { DBusMessage *message; DBusMessageIter iter; int sv[2], ret = -1; char s[MAXPATHLEN] = { 0 }; if (memcmp(&p, &r, sizeof(struct ucred)) != 0) { nih_error("%s: proxy != requestor", __func__); return -1; } if (!(message = start_dbus_request("GetPidCgroupAbsScm", sv))) { nih_error("%s: error starting dbus request", __func__); return -1; } dbus_message_iter_init_append(message, &iter); if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &controller)) { dbus_message_unref(message); nih_error("%s: out of memory", __func__); goto out; } if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_UNIX_FD, &sv[1])) { dbus_message_unref(message); nih_error("%s: out of memory", __func__); goto out; } if (!complete_dbus_request(message, sv, &r, &v)) { nih_error("%s: error completing dbus request", __func__); goto out; } if (proxyrecv(sv[0], s, MAXPATHLEN-1) <= 0) nih_error("%s: Error reading result from cgmanager", __func__); else { *output = NIH_MUST( nih_strdup(parent, s) ); ret = 0; } out: close(sv[0]); close(sv[1]); return ret; }
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; }
int set_value_main(const char *controller, const char *cgroup, const char *key, const char *value, struct ucred p, struct ucred r) { 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_WRONLY)) { nih_error("%s: Pid %d may not access %s\n", __func__, r.pid, path); return -1; } /* read and return the value */ if (!set_value(path, value)) { nih_error("%s: Failed to set value %s to %s", __func__, path, value); return -1; } return 0; }