[lxc-devel] [lxc/master] attach: make attach work with hidepid={1, 2}

brauner on Github lxc-bot at linuxcontainers.org
Mon Dec 18 01:52:16 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 381 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20171218/882f4b00/attachment.bin>
-------------- next part --------------
From 3d7b7a90865c7ed9d0bf39b0938b60d73857c65b Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Mon, 18 Dec 2017 02:46:10 +0100
Subject: [PATCH] attach: make attach work with hidepid={1,2}

Closes #2045.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/attach.c | 214 +++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 184 insertions(+), 30 deletions(-)

diff --git a/src/lxc/attach.c b/src/lxc/attach.c
index d22716199..495ee5656 100644
--- a/src/lxc/attach.c
+++ b/src/lxc/attach.c
@@ -90,10 +90,11 @@ lxc_log_define(lxc_attach, lxc);
 #define __LSMATTRLEN (5 + (LXC_NUMSTRLEN64) + 7 + 1)
 static int lsm_openat(int procfd, pid_t pid, int on_exec)
 {
-	int ret = -1;
-	int labelfd = -1;
+	int saved_errno;
 	const char *name;
 	char path[__LSMATTRLEN];
+	int ret = -1;
+	int labelfd = -1;
 
 	name = lsm_name();
 
@@ -114,12 +115,15 @@ static int lsm_openat(int procfd, pid_t pid, int on_exec)
 	if (ret < 0 || ret >= __LSMATTRLEN)
 		return -1;
 
+	saved_errno = errno;
 	labelfd = openat(procfd, path, O_RDWR);
 	if (labelfd < 0) {
-		SYSERROR("Unable to open file descriptor to set LSM label.");
+		WARN("%s - Unable to open file descriptor to set LSM label", strerror(saved_errno));
+		errno = saved_errno;
 		return -1;
 	}
 
+	errno = saved_errno;
 	return labelfd;
 }
 
@@ -719,7 +723,7 @@ struct attach_clone_payload {
 	void *exec_payload;
 };
 
-static int attach_child_main(void* data);
+static int attach_child_main(void *data);
 
 /* Help the optimizer along if it doesn't know that exit always exits. */
 #define rexit(c)                                                               \
@@ -818,6 +822,148 @@ static signed long get_personality(const char *name, const char *lxcpath)
 	return ret;
 }
 
+struct lsm_label_fd_data {
+	pid_t attached_pid;
+	pid_t attached_pid_in_ns;
+	int lsm_labelfd;
+	lxc_attach_options_t *options;
+	struct lxc_proc_context_info *init_ctx;
+};
+
+static int do_lsm_label_fd_open(int pid, int on_exec)
+{
+	int labelfd, procfd, saved_errno;
+	procfd = open("/proc", O_RDONLY | O_PATH | O_DIRECTORY | O_CLOEXEC | O_PATH);
+	if (procfd < 0) {
+		WARN("%s - Failed to open \"/proc\"", strerror(errno));
+		return -1;
+	}
+
+	labelfd = lsm_openat(procfd, pid, on_exec);
+	saved_errno = errno;
+	close(procfd);
+	if (labelfd < 0) {
+		WARN("%s - Failed to open lsm file descriptor", strerror(saved_errno));
+		errno = saved_errno;
+		return -1;
+	}
+
+	errno = saved_errno;
+	return labelfd;
+}
+
+static int do_lsm_label_fd_get(void *args)
+{
+	int on_exec, ret;
+	struct lsm_label_fd_data *data = args;
+
+	(void)umount2("/proc", MNT_DETACH);
+
+	ret = mount("none", "/proc", "proc", 0, NULL);
+	if (ret < 0) {
+		SYSERROR("Failed to mount \"/proc\" in new mount namespace");
+		return -1;
+	}
+
+	on_exec = data->options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
+	ret = do_lsm_label_fd_open(data->attached_pid_in_ns, on_exec);
+	if (ret < 0) {
+		SYSERROR("Failed to open LSM file descriptor");
+		return -1;
+	}
+	data->lsm_labelfd = ret;
+
+	return 0;
+}
+
+static int do_ns_attach(void *args)
+{
+	int pid, ret;
+	struct lsm_label_fd_data *data = args;
+	uid_t new_uid = 0;
+	gid_t new_gid = 0;
+
+	ret = setns(data->init_ctx->ns_fd[LXC_NS_USER], CLONE_NEWUSER);
+	if (ret < 0) {
+		SYSERROR("Failed to attach to user namespace");
+		return -1;
+	}
+
+	ret = setns(data->init_ctx->ns_fd[LXC_NS_PID], CLONE_NEWPID);
+	if (ret < 0) {
+		SYSERROR("Failed to attach to pid namespace");
+		return -1;
+	}
+
+	lxc_attach_get_init_uidgid(&new_uid, &new_gid);
+
+	if (data->options->uid != (uid_t)-1)
+		new_uid = data->options->uid;
+	if (data->options->gid != (gid_t)-1)
+		new_gid = data->options->gid;
+
+	ret = setgid(new_gid);
+	if (ret < 0) {
+		SYSERROR("Failed to set gid to %d", new_gid);
+		return -1;
+	}
+
+	ret = setgroups(0, NULL);
+	if (ret < 0) {
+		SYSERROR("Failed to drop supplementary groups");
+		return -1;
+	}
+
+	ret = setuid(new_uid);
+	if (ret < 0) {
+		SYSERROR("Failed to set uid to %d", new_uid);
+		return -1;
+	}
+
+	pid = lxc_clone(do_lsm_label_fd_get, data,
+			CLONE_VM | CLONE_FILES | CLONE_NEWNS);
+	if (pid < 0) {
+		SYSERROR("Failed to create new process");
+		return -1;
+	}
+
+	ret = wait_for_pid(pid);
+	if (ret < 0)
+		return -1;
+
+	return 0;
+}
+
+static int get_lsm_label_fd_safe(struct lsm_label_fd_data *l)
+{
+	int on_exec, pid, ret;
+
+	on_exec = l->options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
+	l->lsm_labelfd = do_lsm_label_fd_open(l->attached_pid, on_exec);
+	if (l->lsm_labelfd >= 0)
+		goto out;
+
+	if ((l->options->namespaces & CLONE_NEWUSER) == 0)
+		return -1;
+
+	/* /proc could be mounted with hidepid={1,2} so try something more
+	 * difficult.
+	 */
+	pid = lxc_clone(do_ns_attach, l, CLONE_VM | CLONE_FILES);
+	if (pid < 0) {
+		SYSERROR("Failed to create new process");
+		return -1;
+	}
+
+	ret = wait_for_pid(pid);
+	if (ret < 0)
+		return -1;
+
+out:
+	INFO("Retrieved LSM label file descriptor");
+	return l->lsm_labelfd;
+}
+
 int lxc_attach(const char *name, const char *lxcpath,
 	       lxc_attach_exec_t exec_function, void *exec_payload,
 	       lxc_attach_options_t *options, pid_t *attached_process)
@@ -1003,12 +1149,9 @@ int lxc_attach(const char *name, const char *lxcpath,
 	}
 
 	if (pid) {
-		int procfd = -1;
+		pid_t attached_pid_in_ns;
 		pid_t to_cleanup_pid = pid;
 
-		/* close file namespace descriptors */
-		lxc_proc_close_ns_fd(init_ctx);
-
 		/* Initial thread, we close the socket that is for the
 		 * subprocesses.
 		 */
@@ -1026,15 +1169,6 @@ int lxc_attach(const char *name, const char *lxcpath,
 			if (setup_resource_limits(&init_ctx->container->lxc_conf->limits, pid) < 0)
 				goto on_error;
 
-		/* Open /proc before setns() to the containers namespace so we
-		 * don't rely on any information from inside the container.
-		 */
-		procfd = open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
-		if (procfd < 0) {
-			SYSERROR("Unable to open /proc.");
-			goto on_error;
-		}
-
 		/* Let the child process know to go ahead. */
 		status = 0;
 		ret = lxc_write_nointr(ipc_sockets[0], &status, sizeof(status));
@@ -1053,6 +1187,15 @@ int lxc_attach(const char *name, const char *lxcpath,
 			goto on_error;
 		}
 
+		/* Get pid of attached process in its pid namespace from intermediate process. */
+		ret = lxc_read_nointr_expect(ipc_sockets[0], &attached_pid_in_ns,
+					     sizeof(attached_pid_in_ns), NULL);
+		if (ret <= 0) {
+			if (ret != 0)
+				ERROR("Expected to receive pid: %s.", strerror(errno));
+			goto on_error;
+		}
+
 		/* Ignore SIGKILL (CTRL-C) and SIGQUIT (CTRL-\) - issue #313. */
 		if (options->stdin_fd == 0) {
 			signal(SIGINT, SIG_IGN);
@@ -1109,27 +1252,31 @@ int lxc_attach(const char *name, const char *lxcpath,
 		if ((options->namespaces & CLONE_NEWNS) &&
 		    (options->attach_flags & LXC_ATTACH_LSM) &&
 		    init_ctx->lsm_label) {
-			int on_exec, saved_errno;
-			int labelfd = -1;
-
-			on_exec = options->attach_flags & LXC_ATTACH_LSM_EXEC ? 1 : 0;
-			/* Open fd for the LSM security module. */
-			labelfd = lsm_openat(procfd, attached_pid, on_exec);
-			if (labelfd < 0)
+			int labelfd;
+			int ret = -1;
+			struct lsm_label_fd_data l;
+
+			l.attached_pid = attached_pid;
+			l.attached_pid_in_ns = attached_pid_in_ns;
+			l.options = options;
+			l.init_ctx = init_ctx;
+
+			labelfd = get_lsm_label_fd_safe(&l);
+			if (labelfd < 0) {
+				ERROR("Failed to retrieve LSM label file descriptor %d", labelfd);
 				goto on_error;
+			}
+			TRACE("Sending LSM label file descriptor %d", labelfd);
 
 			/* Send child fd of the LSM security module to write to. */
 			ret = lxc_abstract_unix_send_fds(ipc_sockets[0], &labelfd, 1, NULL, 0);
-			saved_errno = errno;
 			close(labelfd);
 			if (ret <= 0) {
-				ERROR("Intended to send file descriptor %d: %s.", labelfd, strerror(saved_errno));
+				ERROR("Failed to send LSM label file descriptor");
 				goto on_error;
 			}
 		}
 
-		if (procfd >= 0)
-			close(procfd);
 		/* Now shut down communication with child, we're done. */
 		shutdown(ipc_sockets[0], SHUT_RDWR);
 		close(ipc_sockets[0]);
@@ -1147,8 +1294,6 @@ int lxc_attach(const char *name, const char *lxcpath,
 		/* First shut down the socket, then wait for the pid, otherwise
 		 * the pid we're waiting for may never exit.
 		 */
-		if (procfd >= 0)
-			close(procfd);
 		shutdown(ipc_sockets[0], SHUT_RDWR);
 		close(ipc_sockets[0]);
 		if (to_cleanup_pid)
@@ -1258,6 +1403,15 @@ static int attach_child_main(void* data)
 	lxc_attach_options_t* options = payload->options;
 	struct lxc_proc_context_info* init_ctx = payload->init_ctx;
 
+	/* Tell parent the pid in our pid namespace. */
+	status = syscall(SYS_getpid);
+	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's ready for us to
 	 * start initializing.
 	 */


More information about the lxc-devel mailing list