Ejemplo n.º 1
0
/*
 * lxc_cmd_rsp_recv: Receive a response to a command
 *
 * @sock  : the socket connected to the container
 * @cmd   : command to put response in
 *
 * Returns the size of the response message or < 0 on failure
 *
 * Note that if the command response datalen > 0, then data is
 * a malloc()ed buffer and should be free()ed by the caller. If
 * the response data is <= a void * worth of data, it will be
 * stored directly in data and datalen will be 0.
 *
 * As a special case, the response for LXC_CMD_CONSOLE is created
 * here as it contains an fd for the master pty passed through the
 * unix socket.
 */
static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
{
	int ret, rspfd;
	struct lxc_cmd_rsp *rsp = &cmd->rsp;

	ret = lxc_abstract_unix_recv_fds(sock, &rspfd, 1, rsp, sizeof(*rsp));
	if (ret < 0) {
		SYSWARN("Failed to receive response for command \"%s\"",
		        lxc_cmd_str(cmd->req.cmd));

		if (errno == ECONNRESET)
			return -1;

		return -1;
	}
	TRACE("Command \"%s\" received response", lxc_cmd_str(cmd->req.cmd));

	if (cmd->req.cmd == LXC_CMD_CONSOLE) {
		struct lxc_cmd_console_rsp_data *rspdata;

		/* recv() returns 0 bytes when a tty cannot be allocated,
		 * rsp->ret is < 0 when the peer permission check failed
		 */
		if (ret == 0 || rsp->ret < 0)
			return 0;

		rspdata = malloc(sizeof(*rspdata));
		if (!rspdata) {
			errno = ENOMEM;
			ERROR("Failed to allocate response buffer for command \"%s\"",
			      lxc_cmd_str(cmd->req.cmd));
			return -1;
		}

		rspdata->masterfd = rspfd;
		rspdata->ttynum = PTR_TO_INT(rsp->data);
		rsp->data = rspdata;
	}

	if (rsp->datalen == 0) {
		DEBUG("Response data length for command \"%s\" is 0",
		      lxc_cmd_str(cmd->req.cmd));
		return ret;
	}

	if ((rsp->datalen > LXC_CMD_DATA_MAX) &&
	    (cmd->req.cmd != LXC_CMD_CONSOLE_LOG)) {
		ERROR("Response data for command \"%s\" is too long: %d bytes > %d",
		      lxc_cmd_str(cmd->req.cmd), rsp->datalen, LXC_CMD_DATA_MAX);
		return -1;
	}

	if (cmd->req.cmd == LXC_CMD_CONSOLE_LOG) {
		rsp->data = malloc(rsp->datalen + 1);
		((char *)rsp->data)[rsp->datalen] = '\0';
	} else {
		rsp->data = malloc(rsp->datalen);
	}
	if (!rsp->data) {
		errno = ENOMEM;
		ERROR("Failed to allocate response buffer for command \"%s\"",
		      lxc_cmd_str(cmd->req.cmd));
		return -1;
	}

	ret = lxc_recv_nointr(sock, rsp->data, rsp->datalen, 0);
	if (ret != rsp->datalen) {
		SYSERROR("Failed to receive response data for command \"%s\"",
		         lxc_cmd_str(cmd->req.cmd));
		return -1;
	}

	return ret;
}
Ejemplo n.º 2
0
static int attach_child_main(void* data)
{
	struct attach_clone_payload* payload = (struct attach_clone_payload*)data;
	int ipc_socket = payload->ipc_socket;
	lxc_attach_options_t* options = payload->options;
	struct lxc_proc_context_info* init_ctx = payload->init_ctx;
#if HAVE_SYS_PERSONALITY_H
	long new_personality;
#endif
	int ret;
	int status;
	int expected;
	long flags;
	int fd;
	int lsm_labelfd;
	uid_t new_uid;
	gid_t new_gid;

	/* Wait for the initial thread to signal us that it's ready for us to
	 * start initializing.
	 */
	expected = 0;
	status = -1;
	ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected);
	if (ret <= 0) {
		ERROR("Expected to receive sequence number 0: %s.", strerror(errno));
		shutdown(ipc_socket, SHUT_RDWR);
		rexit(-1);
	}

	/* A description of the purpose of this functionality is provided in the
	 * lxc-attach(1) manual page. We have to remount here and not in the
	 * parent process, otherwise /proc may not properly reflect the new pid
	 * namespace.
	 */
	if (!(options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_REMOUNT_PROC_SYS)) {
		ret = lxc_attach_remount_sys_proc();
		if (ret < 0) {
			shutdown(ipc_socket, SHUT_RDWR);
			rexit(-1);
		}
	}

	/* Now perform additional attachments. */
#if HAVE_SYS_PERSONALITY_H
	if (options->personality < 0)
		new_personality = init_ctx->personality;
	else
		new_personality = options->personality;

	if (options->attach_flags & LXC_ATTACH_SET_PERSONALITY) {
		ret = personality(new_personality);
		if (ret < 0) {
			SYSERROR("Could not ensure correct architecture.");
			shutdown(ipc_socket, SHUT_RDWR);
			rexit(-1);
		}
	}
#endif

	if (options->attach_flags & LXC_ATTACH_DROP_CAPABILITIES) {
		ret = lxc_attach_drop_privs(init_ctx);
		if (ret < 0) {
			ERROR("Could not drop privileges.");
			shutdown(ipc_socket, SHUT_RDWR);
			rexit(-1);
		}
	}

	/* Always set the environment (specify (LXC_ATTACH_KEEP_ENV, NULL, NULL)
	 * if you want this to be a no-op).
	 */
	ret = lxc_attach_set_environment(options->env_policy, options->extra_env_vars, options->extra_keep_env);
	if (ret < 0) {
		ERROR("Could not set initial environment for attached process.");
		shutdown(ipc_socket, SHUT_RDWR);
		rexit(-1);
	}

	/* Set {u,g}id. */
	new_uid = 0;
	new_gid = 0;
	/* Ignore errors, we will fall back to root in that case (/proc was not
	 * mounted etc.).
	 */
	if (options->namespaces & CLONE_NEWUSER)
		lxc_attach_get_init_uidgid(&new_uid, &new_gid);

	if (options->uid != (uid_t)-1)
		new_uid = options->uid;
	if (options->gid != (gid_t)-1)
		new_gid = options->gid;

	/* Setup the controlling tty. */
	if (options->stdin_fd && isatty(options->stdin_fd)) {
		if (setsid() < 0) {
			SYSERROR("Unable to setsid.");
			shutdown(ipc_socket, SHUT_RDWR);
			rexit(-1);
		}

		if (ioctl(options->stdin_fd, TIOCSCTTY, (char *)NULL) < 0) {
			SYSERROR("Unable to set TIOCSTTY.");
			shutdown(ipc_socket, SHUT_RDWR);
			rexit(-1);
		}
	}

	/* Try to set the {u,g}id combination. */
	if ((new_gid != 0 || options->namespaces & CLONE_NEWUSER)) {
		if (setgid(new_gid) || setgroups(0, NULL)) {
			SYSERROR("Switching to container gid.");
			shutdown(ipc_socket, SHUT_RDWR);
			rexit(-1);
		}
	}
	if ((new_uid != 0 || options->namespaces & CLONE_NEWUSER) && setuid(new_uid)) {
		SYSERROR("Switching to container uid.");
		shutdown(ipc_socket, SHUT_RDWR);
		rexit(-1);
	}

	/* Tell initial process it may now put us into cgroups. */
	status = 1;
	ret = lxc_write_nointr(ipc_socket, &status, sizeof(status));
	if (ret != sizeof(status)) {
		ERROR("Intended to send sequence number 1: %s.", strerror(errno));
		shutdown(ipc_socket, SHUT_RDWR);
		rexit(-1);
	}

	/* Wait for the initial thread to signal us that it has done everything
	 * for us when it comes to cgroups etc.
	 */
	expected = 2;
	status = -1;
	ret = lxc_read_nointr_expect(ipc_socket, &status, sizeof(status), &expected);
	if (ret <= 0) {
		ERROR("Expected to receive sequence number 2: %s", strerror(errno));
		shutdown(ipc_socket, SHUT_RDWR);
		rexit(-1);
	}

	if ((init_ctx->container && init_ctx->container->lxc_conf &&
	     init_ctx->container->lxc_conf->no_new_privs) ||
	    (options->attach_flags & LXC_ATTACH_NO_NEW_PRIVS)) {
		if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
			SYSERROR("PR_SET_NO_NEW_PRIVS could not be set. "
				 "Process can use execve() gainable "
				 "privileges.");
			shutdown(ipc_socket, SHUT_RDWR);
			rexit(-1);
		}
		INFO("PR_SET_NO_NEW_PRIVS is set. Process cannot use execve() "
		     "gainable privileges.");
	}

	/* Tell the (grand)parent to send us LSM label fd. */
	status = 3;
	ret = lxc_write_nointr(ipc_socket, &status, sizeof(status));
	if (ret <= 0) {
		ERROR("Intended to send sequence number 3: %s.", strerror(errno));
		shutdown(ipc_socket, SHUT_RDWR);
		rexit(-1);
	}

	if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_LSM) && init_ctx->lsm_label) {
		int on_exec;
		/* Receive fd for LSM security module. */
		ret = lxc_abstract_unix_recv_fds(ipc_socket, &lsm_labelfd, 1, NULL, 0);
		if (ret <= 0) {
			ERROR("Expected to receive file descriptor: %s.", strerror(errno));
			shutdown(ipc_socket, SHUT_RDWR);
			rexit(-1);
		}

		/* Change into our new LSM profile. */
		on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
		if (lsm_set_label_at(lsm_labelfd, on_exec, init_ctx->lsm_label) < 0) {
			SYSERROR("Failed to set LSM label.");
			shutdown(ipc_socket, SHUT_RDWR);
			close(lsm_labelfd);
			rexit(-1);
		}
		close(lsm_labelfd);
	}

	if (init_ctx->container && init_ctx->container->lxc_conf &&
	    init_ctx->container->lxc_conf->seccomp &&
	    (lxc_seccomp_load(init_ctx->container->lxc_conf) != 0)) {
		ERROR("Failed to load seccomp policy.");
		shutdown(ipc_socket, SHUT_RDWR);
		rexit(-1);
	}

	shutdown(ipc_socket, SHUT_RDWR);
	close(ipc_socket);
	lxc_proc_put_context_info(init_ctx);

	/* The following is done after the communication socket is shut down.
	 * That way, all errors that might (though unlikely) occur up until this
	 * point will have their messages printed to the original stderr (if
	 * logging is so configured) and not the fd the user supplied, if any.
	 */

	/* Fd handling for stdin, stdout and stderr; ignore errors here, user
	 * may want to make sure the fds are closed, for example.
	 */
	if (options->stdin_fd >= 0 && options->stdin_fd != 0)
		dup2(options->stdin_fd, 0);
	if (options->stdout_fd >= 0 && options->stdout_fd != 1)
		dup2(options->stdout_fd, 1);
	if (options->stderr_fd >= 0 && options->stderr_fd != 2)
		dup2(options->stderr_fd, 2);

	/* close the old fds */
	if (options->stdin_fd > 2)
		close(options->stdin_fd);
	if (options->stdout_fd > 2)
		close(options->stdout_fd);
	if (options->stderr_fd > 2)
		close(options->stderr_fd);

	/* Try to remove FD_CLOEXEC flag from stdin/stdout/stderr, but also
	 * here, ignore errors.
	 */
	for (fd = 0; fd <= 2; fd++) {
		flags = fcntl(fd, F_GETFL);
		if (flags < 0)
			continue;
		if (flags & FD_CLOEXEC)
			if (fcntl(fd, F_SETFL, flags & ~FD_CLOEXEC) < 0)
				SYSERROR("Unable to clear FD_CLOEXEC from file descriptor.");
	}

	/* We're done, so we can now do whatever the user intended us to do. */
	rexit(payload->exec_function(payload->exec_payload));
}