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); }
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; }
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 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; }
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; }
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 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; }
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 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); }
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 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; }
int list_children_main (void *parent, char *controller, const char *cgroup, struct ucred p, struct ucred r, char ***output) { DBusMessage *message; DBusMessageIter iter; int sv[2], ret = -1; uint32_t len; int32_t nrkids; nih_local char * paths = NULL; char *s; int i; *output = NULL; 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("ListChildrenScm", 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_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], &nrkids, sizeof(int32_t)) != sizeof(int32_t)) goto out; if (nrkids == 0) { ret = 0; goto out; } if (nrkids < 0) { nih_error("%s: Server encountered an error: bad cgroup?", __func__); ret = -1; goto out; } if (proxyrecv(sv[0], &len, sizeof(uint32_t)) != sizeof(uint32_t)) goto out; paths = nih_alloc(NULL, len+1); paths[len] = '\0'; if (read(sv[0], paths, len) != len) { nih_error("%s: Failed getting paths from server", __func__); goto out; } *output = NIH_MUST( nih_alloc(parent, sizeof( char*)*(nrkids+1)) ); memset(*output, 0, (nrkids + 1) * sizeof(char *)); s = paths; for (i=0; i<nrkids; i++) { if (s > paths + len) { ret = -1; nih_error("%s: corrupted result from cgmanager", __func__); goto out; } (*output)[i] = NIH_MUST( nih_strdup(parent, s) ); s += strlen(s) + 1; } ret = nrkids; out: close(sv[0]); close(sv[1]); return ret; }
int get_tasks_recursive_main (void *parent, const char *controller, const char *cgroup, struct ucred p, struct ucred r, int32_t **pids) { DBusMessage *message; DBusMessageIter iter; int sv[2], ret = -1; uint32_t nrpids; struct ucred tcred; int i; 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("GetTasksRecursiveScm", 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_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], &nrpids, sizeof(uint32_t)) != sizeof(uint32_t)) goto out; if (nrpids == -1) { nih_error("%s: bad cgroup: %s:%s", __func__, controller, cgroup); ret = -1; goto out; } if (nrpids == 0) { ret = 0; goto out; } *pids = NIH_MUST( nih_alloc(parent, nrpids * sizeof(uint32_t)) ); for (i=0; i<nrpids; i++) { get_scm_creds_sync(sv[0], &tcred); if (tcred.pid == -1) { nih_warn("%s: Failed getting pid from server", __func__); goto out; } (*pids)[i] = tcred.pid; } ret = nrpids; out: close(sv[0]); close(sv[1]); return ret; }
int set_value_main (char *controller, const char *cgroup, const char *key, const char *value, struct ucred p, struct ucred r) { 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("SetValueScm", 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_STRING, &key)) { nih_error("%s: out of memory", __func__); dbus_message_unref(message); goto out; } if (! dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &value)) { 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') ret = 0; out: close(sv[0]); close(sv[1]); return ret; }
int get_value_main (void *parent, char *controller, const char *cgroup, const char *key, struct ucred p, struct ucred r, char **value) { DBusMessage *message; DBusMessageIter iter; int sv[2], ret = -1; char output[MAXPATHLEN] = { 0 }; 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("GetValueScm", 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_STRING, &key)) { 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], output, MAXPATHLEN) <= 0) { nih_error("%s: Failed reading string from cgmanager: %s", __func__, strerror(errno)); } else { *value = NIH_MUST( nih_strdup(parent, output) ); ret = 0; } out: close(sv[0]); close(sv[1]); return ret; }
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 list_keys_main (void *parent, char *controller, const char *cgroup, struct ucred p, struct ucred r, struct keys_return_type ***output) { DBusMessage *message; DBusMessageIter iter; int sv[2], ret = -1; uint32_t len; int32_t nrkeys; nih_local char * results = NULL; char *s; int i; *output = NULL; 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("ListKeysScm", 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_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], &nrkeys, sizeof(int32_t)) != sizeof(int32_t)) goto out; if (nrkeys == 0) { ret = 0; goto out; } if (nrkeys < 0) { nih_error("%s: Server encountered an error: bad cgroup?", __func__); ret = -1; goto out; } if (proxyrecv(sv[0], &len, sizeof(uint32_t)) != sizeof(uint32_t)) goto out; results = nih_alloc(NULL, len+1); results[len] = '\0'; if (read(sv[0], results, len) != len) { nih_error("%s: Failed getting results from server", __func__); goto out; } *output = NIH_MUST( nih_alloc(parent, sizeof(**output)*(nrkeys+1)) ); memset(*output, 0, (nrkeys + 1) * sizeof(**output)); s = results; for (i=0; i<nrkeys; i++) { struct keys_return_type *tmp; char *s2 = find_eol(s); if (s2 > results + len) goto bad; *s2 = '\0'; (*output)[i] = tmp = NIH_MUST( nih_new(*output, struct keys_return_type) ); tmp->name = NIH_MUST( nih_strdup(tmp, s) ); s = s2 + 1; s2 = find_eol(s); if (s2 > results + len) goto bad; if (sscanf(s, "%u\n", &tmp->uid) != 1) goto bad; s = s2 + 1; s2 = find_eol(s); if (sscanf(s, "%u\n", &tmp->gid) != 1) goto bad; s = s2 + 1; s2 = find_eol(s); if (sscanf(s, "%u\n", &tmp->perms) != 1) goto bad; s = s2 + 1; } ret = nrkeys; out: close(sv[0]); close(sv[1]); return ret; bad: ret = -1; nih_error("%s: corrupted result from cgmanager", __func__); goto out; }