Example #1
0
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;
}
Example #2
0
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);
}
Example #3
0
/*
 * 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();
}
Example #4
0
/* 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;
}
Example #5
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);
}
Example #6
0
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;
}
Example #7
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;
}
Example #8
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));
	}
}
Example #9
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");
}
Example #10
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;
}
Example #11
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;
}
Example #12
0
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;
}
Example #13
0
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;
}
Example #14
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;
}
Example #15
0
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);
	}
}
Example #16
0
/**
 * 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);
}
Example #17
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;
}
Example #18
0
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);
}
Example #19
0
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;
}
Example #20
0
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;
}
Example #21
0
/*
 * 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;
}
Example #22
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;
}
Example #23
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");
}
Example #24
0
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);
}
Example #25
0
/*
 * 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;
}
Example #26
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;
}
Example #27
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;
}
Example #28
0
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;
}
Example #29
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;
}
Example #30
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;
}