[lxc-devel] [lxc/master] lxc-usernsexec: lxc-usernsexec: introduce and use userns_exec() and implement -b, -u, and -g arguments

brauner on Github lxc-bot at linuxcontainers.org
Thu May 21 10:03:33 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 364 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200521/2d3e0a31/attachment-0001.bin>
-------------- next part --------------
From 1609452e3a0554fef5e688146f6095a158d30c5c Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 21 May 2020 10:25:34 +0200
Subject: [PATCH 1/5] utils_no_static: add filer for helpers that cannot be
 statically linked

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/Makefile.am |  3 +++
 src/lxc/conf.c      | 57 +--------------------------------------------
 2 files changed, 4 insertions(+), 56 deletions(-)

diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index d1e23647e0..f99fb2520c 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -50,6 +50,7 @@ noinst_HEADERS = api_extensions.h \
 		 tools/arguments.h \
 		 storage/storage_utils.h \
 		 utils.h \
+		 utils_no_static.h \
 		 uuid.h
 
 if IS_BIONIC
@@ -150,6 +151,7 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \
 		    syscall_wrappers.h \
 		    terminal.c \
 		    utils.c utils.h \
+		    utils_no_static.c utils_no_static.h \
 		    uuid.c uuid.h \
 		    version.h \
 		    $(LSM_SOURCES)
@@ -418,6 +420,7 @@ lxc_usernsexec_SOURCES = cmd/lxc_usernsexec.c \
 			 memory_utils.h \
 			 string_utils.c string_utils.h \
 			 syscall_wrappers.h \
+			 utils_no_static.c utils_no_static.h
 			 utils.c utils.h
 endif
 
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 5cbca60006..e4408201d4 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -59,6 +59,7 @@
 #include "syscall_wrappers.h"
 #include "terminal.h"
 #include "utils.h"
+#include "utils_no_static.h"
 #include "uuid.h"
 
 #ifdef MAJOR_IN_MKDEV
@@ -4687,62 +4688,6 @@ int userns_exec_mapped_root(const char *path, int path_fd,
 	return wait_for_pid(pid);
 }
 
-/* not thread-safe, do not use from api without first forking */
-static char *getuname(void)
-{
-	__do_free char *buf = NULL;
-	struct passwd pwent;
-	struct passwd *pwentp = NULL;
-	size_t bufsize;
-	int ret;
-
-	bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
-	if (bufsize == -1)
-		bufsize = 1024;
-
-	buf = malloc(bufsize);
-	if (!buf)
-		return NULL;
-
-	ret = getpwuid_r(geteuid(), &pwent, buf, bufsize, &pwentp);
-	if (!pwentp) {
-		if (ret == 0)
-			WARN("Could not find matched password record.");
-
-		return log_error(NULL, "Failed to get password record - %u", geteuid());
-	}
-
-	return strdup(pwent.pw_name);
-}
-
-/* not thread-safe, do not use from api without first forking */
-static char *getgname(void)
-{
-	__do_free char *buf = NULL;
-	struct group grent;
-	struct group *grentp = NULL;
-	size_t bufsize;
-	int ret;
-
-	bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
-	if (bufsize == -1)
-		bufsize = 1024;
-
-	buf = malloc(bufsize);
-	if (!buf)
-		return NULL;
-
-	ret = getgrgid_r(getegid(), &grent, buf, bufsize, &grentp);
-	if (!grentp) {
-		if (ret == 0)
-			WARN("Could not find matched group record");
-
-		return log_error(NULL, "Failed to get group record - %u", getegid());
-	}
-
-	return strdup(grent.gr_name);
-}
-
 /* not thread-safe, do not use from api without first forking */
 void suggest_default_idmap(void)
 {

From d713543f2a27a6d406b98081d4683453c4d19a5c Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 21 May 2020 10:32:15 +0200
Subject: [PATCH 2/5] lxc-usernsexec: always mount MS_SLAVE | MS_REC

There's no point in not doing it and checking before.

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/cmd/lxc_usernsexec.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/lxc/cmd/lxc_usernsexec.c b/src/lxc/cmd/lxc_usernsexec.c
index 3c22482f4f..63a59a8d4c 100644
--- a/src/lxc/cmd/lxc_usernsexec.c
+++ b/src/lxc/cmd/lxc_usernsexec.c
@@ -100,12 +100,10 @@ static int do_child(void *vargv)
 		return -1;
 	}
 
-	if (detect_shared_rootfs()) {
-		ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL);
-		if (ret < 0) {
-			CMD_SYSINFO("Failed to make \"/\" rslave");
-			return -1;
-		}
+	ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL);
+	if (ret) {
+		CMD_SYSINFO("Failed to make \"/\" rslave");
+		return -1;
 	}
 
 	execvp(argv[0], argv);

From 776cf48433d9852015e9529f136bd4dedff6264e Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 21 May 2020 10:34:01 +0200
Subject: [PATCH 3/5] lxc-usernsexec: setgroups() can legitimately fail when
 setgroups == "deny"

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/cmd/lxc_usernsexec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lxc/cmd/lxc_usernsexec.c b/src/lxc/cmd/lxc_usernsexec.c
index 63a59a8d4c..1166ef3977 100644
--- a/src/lxc/cmd/lxc_usernsexec.c
+++ b/src/lxc/cmd/lxc_usernsexec.c
@@ -87,7 +87,7 @@ static int do_child(void *vargv)
 	int ret;
 	char **argv = (char **)vargv;
 
-	if (!lxc_setgroups(0, NULL))
+	if (setgroups(0, NULL) && errno != EPERM)
 		return -1;
 
 	/* Assume we want to become root */

From 221a471445df22453a902279162a9425893191ec Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 21 May 2020 10:34:30 +0200
Subject: [PATCH 4/5] lxc-usernsexec: remove outdated comment

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/cmd/lxc_usernsexec.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/lxc/cmd/lxc_usernsexec.c b/src/lxc/cmd/lxc_usernsexec.c
index 1166ef3977..65dc82e311 100644
--- a/src/lxc/cmd/lxc_usernsexec.c
+++ b/src/lxc/cmd/lxc_usernsexec.c
@@ -80,7 +80,6 @@ static void opentty(const char *tty, int which)
 		close(fd);
 	}
 }
-/* Code copy end */
 
 static int do_child(void *vargv)
 {

From 780abb703c33b44790c38e9554aae9893657a8c9 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Thu, 21 May 2020 11:53:03 +0200
Subject: [PATCH 5/5] lxc-usernsexec: introduce and use userns_exec() and
 implement -b, -u, and -g arguments

lxc-usernsexec -mb1:1000:1 -b 1
lxc-usernsexec -mb1:1000:1 -u 1 -g 1

Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
 src/lxc/cmd/lxc_usernsexec.c | 199 ++++++++++++-----------------------
 src/lxc/conf.c               |  80 ++++++++++++++
 src/lxc/conf.h               |   3 +
 3 files changed, 150 insertions(+), 132 deletions(-)

diff --git a/src/lxc/cmd/lxc_usernsexec.c b/src/lxc/cmd/lxc_usernsexec.c
index 65dc82e311..86b980cbdb 100644
--- a/src/lxc/cmd/lxc_usernsexec.c
+++ b/src/lxc/cmd/lxc_usernsexec.c
@@ -41,6 +41,12 @@ static void usage(const char *name)
 	printf("\n");
 	printf("  -m <uid-maps> uid maps to use\n");
 	printf("\n");
+	printf("  -b <id> uid and gid to use\n");
+	printf("\n");
+	printf("  -u <id> uid to use\n");
+	printf("\n");
+	printf("  -g <id> gid to use\n");
+	printf("\n");
 	printf("  -s:           map self\n");
 	printf("  uid-maps: [u|g|b]:ns_id:host_id:range\n");
 	printf("            [u|g|b]: map user id, group id, or both\n");
@@ -81,32 +87,28 @@ static void opentty(const char *tty, int which)
 	}
 }
 
-static int do_child(void *vargv)
-{
-	int ret;
-	char **argv = (char **)vargv;
+struct child_args {
+	char **argv;
+	char *tty_stdin;
+	char *tty_stdout;
+	char *tty_stderr;
+};
 
-	if (setgroups(0, NULL) && errno != EPERM)
-		return -1;
-
-	/* Assume we want to become root */
-	if (!lxc_switch_uid_gid(0, 0))
-		return -1;
+static int do_child(void *payload)
+{
+	struct child_args *args = payload;
 
-	ret = unshare(CLONE_NEWNS);
-	if (ret < 0) {
-		CMD_SYSERROR("Failed to unshare mount namespace");
+	if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL)) {
+		CMD_SYSERROR("Failed to remount \"/\"");
 		return -1;
 	}
 
-	ret = mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL);
-	if (ret) {
-		CMD_SYSINFO("Failed to make \"/\" rslave");
-		return -1;
-	}
+	opentty(args->tty_stdin, STDIN_FILENO);
+	opentty(args->tty_stdout, STDOUT_FILENO);
+	opentty(args->tty_stderr, STDERR_FILENO);
 
-	execvp(argv[0], argv);
-	CMD_SYSERROR("Failed to execute \"%s\"", argv[0]);
+	execvp(args->argv[0], args->argv);
+	CMD_SYSERROR("Failed to execute \"%s\"", args->argv[0]);
 	return -1;
 }
 
@@ -301,14 +303,17 @@ static bool do_map_self(void)
 
 int main(int argc, char *argv[])
 {
-	int c, pid, ret, status;
-	char buf[1];
-	int pipe_fds1[2], /* child tells parent it has unshared */
-	    pipe_fds2[2]; /* parent tells child it is mapped and may proceed */
-	unsigned long flags = CLONE_NEWUSER | CLONE_NEWNS;
+	int c, ret;
 	char ttyname0[256] = {0}, ttyname1[256] = {0}, ttyname2[256] = {0};
 	char *default_args[] = {"/bin/sh", NULL};
 	bool map_self = false;
+	uid_t nsuid = 0;
+	gid_t nsgid = 0;
+	struct child_args args = {
+		.tty_stdin	= ttyname0,
+		.tty_stdout	= ttyname1,
+		.tty_stderr	= ttyname2,
+	};
 
 	lxc_log_fd = STDERR_FILENO;
 
@@ -334,8 +339,29 @@ int main(int argc, char *argv[])
 
 	lxc_list_init(&active_map);
 
-	while ((c = getopt(argc, argv, "m:hs")) != EOF) {
+	while ((c = getopt(argc, argv, "b:g:hm:su:")) != EOF) {
+		int val;
 		switch (c) {
+		case 'b':
+			ret = lxc_safe_int(optarg, &val);
+			if (ret) {
+				fprintf(stderr, "%m - Failed to parse gid %s\n", optarg);
+				_exit(EXIT_FAILURE);
+			}
+			nsuid = (val < 0) ? LXC_INVALID_UID : val;
+			nsgid = nsuid;
+			break;
+		case 'g':
+			ret = lxc_safe_int(optarg, &val);
+			if (ret) {
+				fprintf(stderr, "%m - Failed to parse gid %s\n", optarg);
+				_exit(EXIT_FAILURE);
+			}
+			nsgid = (val < 0) ? LXC_INVALID_GID : val;
+			break;
+		case 'h':
+			usage(argv[0]);
+			_exit(EXIT_SUCCESS);
 		case 'm':
 			ret = parse_map(optarg);
 			if (ret < 0) {
@@ -343,12 +369,17 @@ int main(int argc, char *argv[])
 				_exit(EXIT_FAILURE);
 			}
 			break;
-		case 'h':
-			usage(argv[0]);
-			_exit(EXIT_SUCCESS);
 		case 's':
 			map_self = true;
 			break;
+		case 'u':
+			ret = lxc_safe_int(optarg, &val);
+			if (ret) {
+				fprintf(stderr, "%m - Failed to parse uid %s\n", optarg);
+				_exit(EXIT_FAILURE);
+			}
+			nsuid = (val < 0) ? LXC_INVALID_UID : val;
+			break;
 		default:
 			usage(argv[0]);
 			_exit(EXIT_FAILURE);
@@ -363,8 +394,10 @@ int main(int argc, char *argv[])
 		}
 	}
 
-	// Do we want to support map-self with no other allocations?
-	// If so we should move this above the previous block.
+	/*
+	 * Do we want to support map-self with no other allocations?
+	 * If so we should move this above the previous block.
+	 */
 	if (map_self) {
 		if (!do_map_self()) {
 			fprintf(stderr, "Failed mapping own uid\n");
@@ -376,106 +409,8 @@ int main(int argc, char *argv[])
 	argc = argc - optind;
 	if (argc < 1)
 		argv = default_args;
+	args.argv = argv;
 
-	ret = pipe2(pipe_fds1, O_CLOEXEC);
-	if (ret < 0) {
-		CMD_SYSERROR("Failed to open new pipe");
-		_exit(EXIT_FAILURE);
-	}
-
-	ret = pipe2(pipe_fds2, O_CLOEXEC);
-	if (ret < 0) {
-		CMD_SYSERROR("Failed to open new pipe");
-		close(pipe_fds1[0]);
-		close(pipe_fds1[1]);
-		_exit(EXIT_FAILURE);
-	}
-
-	pid = fork();
-	if (pid < 0) {
-		close(pipe_fds1[0]);
-		close(pipe_fds1[1]);
-		close(pipe_fds2[0]);
-		close(pipe_fds2[1]);
-		_exit(EXIT_FAILURE);
-	}
-
-	if (pid == 0) {
-		close(pipe_fds1[0]);
-		close(pipe_fds2[1]);
-
-		opentty(ttyname0, STDIN_FILENO);
-		opentty(ttyname1, STDOUT_FILENO);
-		opentty(ttyname2, STDERR_FILENO);
-
-		ret = unshare(flags);
-		if (ret < 0) {
-			CMD_SYSERROR("Failed to unshare mount and user namespace");
-			close(pipe_fds1[1]);
-			close(pipe_fds2[0]);
-			_exit(EXIT_FAILURE);
-		}
-
-		buf[0] = '1';
-		ret = lxc_write_nointr(pipe_fds1[1], buf, 1);
-		if (ret != 1) {
-			CMD_SYSERROR("Failed to write to pipe file descriptor %d",
-				     pipe_fds1[1]);
-			close(pipe_fds1[1]);
-			close(pipe_fds2[0]);
-			_exit(EXIT_FAILURE);
-		}
-
-		ret = lxc_read_nointr(pipe_fds2[0], buf, 1);
-		if (ret != 1) {
-			CMD_SYSERROR("Failed to read from pipe file descriptor %d",
-				     pipe_fds2[0]);
-			close(pipe_fds1[1]);
-			close(pipe_fds2[0]);
-			_exit(EXIT_FAILURE);
-		}
-
-		close(pipe_fds1[1]);
-		close(pipe_fds2[0]);
-
-		if (buf[0] != '1') {
-			fprintf(stderr, "Received unexpected value from parent process\n");
-			_exit(EXIT_FAILURE);
-		}
-
-		ret = do_child((void *)argv);
-		if (ret < 0)
-			_exit(EXIT_FAILURE);
-
-		_exit(EXIT_SUCCESS);
-	}
-
-	close(pipe_fds1[1]);
-	close(pipe_fds2[0]);
-
-	ret = lxc_read_nointr(pipe_fds1[0], buf, 1);
-	if (ret <= 0) {
-		CMD_SYSERROR("Failed to read from pipe file descriptor %d", pipe_fds1[0]);
-		_exit(EXIT_FAILURE);
-	}
-
-	buf[0] = '1';
-
-	ret = lxc_map_ids(&active_map, pid);
-	if (ret < 0)
-		fprintf(stderr, "Failed to write id mapping for child process\n");
-
-	ret = lxc_write_nointr(pipe_fds2[1], buf, 1);
-	if (ret < 0) {
-		CMD_SYSERROR("Failed to write to pipe file descriptor %d", pipe_fds2[1]);
-		_exit(EXIT_FAILURE);
-	}
-
-	ret = waitpid(pid, &status, __WALL);
-	if (ret < 0) {
-		CMD_SYSERROR("Failed to wait on child process");
-		_exit(EXIT_FAILURE);
-	}
-
-	_exit(WEXITSTATUS(status));
+	_exit(userns_exec(CLONE_NEWUSER | CLONE_NEWNS, &active_map, do_child,
+			  &args, nsuid, nsgid));
 }
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index e4408201d4..ae15cf81c7 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -4074,6 +4074,86 @@ static struct lxc_list *get_minimal_idmap(const struct lxc_conf *conf,
 	return move_ptr(idmap);
 }
 
+int userns_exec(unsigned int flags, struct lxc_list *idmap,
+		int (*fn_child)(void *), void *fn_child_data,
+		uid_t nsuid, gid_t nsgid)
+{
+	char c = '1';
+	ssize_t ret;
+	pid_t pid;
+	int sock_fds[2];
+
+	if (!fn_child)
+		return ret_errno(EINVAL);
+
+	ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, sock_fds);
+	if (ret < 0)
+		return -errno;
+
+	pid = lxc_raw_clone(CLONE_NEWUSER | CLONE_NEWNS, NULL);
+	if (pid < 0) {
+		SYSERROR("Failed to create new process");
+		goto on_error;
+	}
+
+	if (pid == 0) {
+		close_prot_errno_disarm(sock_fds[1]);
+
+		ret = lxc_read_nointr(sock_fds[0], &c, 1);
+		if (ret != 1)
+			_exit(EXIT_FAILURE);
+
+		close_prot_errno_disarm(sock_fds[0]);
+
+		if (!setgroups(0, NULL) && errno != EPERM)
+			_exit(EXIT_FAILURE);
+
+		if (gid_valid(nsgid) && setresgid(nsgid, nsgid, nsgid)) {
+			SYSERROR("Failed to setresgid(%d, %d, %d)", nsgid, nsgid, nsgid);
+			_exit(EXIT_FAILURE);
+		}
+
+		if (uid_valid(nsuid) && setresuid(nsuid, nsuid, nsuid)) {
+			SYSERROR("Failed to setresuid(%d, %d, %d)", nsuid, nsuid, nsuid);
+			_exit(EXIT_FAILURE);
+		}
+
+		ret = fn_child(fn_child_data);
+		if (ret) {
+			SYSERROR("Running function in new user namespace failed");
+			_exit(EXIT_FAILURE);
+		}
+
+		_exit(EXIT_SUCCESS);
+	}
+
+	close_prot_errno_disarm(sock_fds[0]);
+
+	/* Set up {g,u}id mapping for user namespace of child process. */
+	ret = lxc_map_ids(idmap, pid);
+	if (ret < 0) {
+		ERROR("Error setting up {g,u}id mappings for child process \"%d\"", pid);
+		goto on_error;
+	}
+
+	/* Tell child to proceed. */
+	ret = lxc_write_nointr(sock_fds[1], &c, 1);
+	if (ret != 1) {
+		SYSERROR("Failed telling child process \"%d\" to proceed", pid);
+		goto on_error;
+	}
+
+on_error:
+	close_prot_errno_disarm(sock_fds[0]);
+	close_prot_errno_disarm(sock_fds[1]);
+
+	/* Wait for child to finish. */
+	if (pid < 0)
+		return -1;
+
+	return wait_for_pid(pid);
+}
+
 /*
  * Run a function in a new user namespace.
  * The caller's euid/egid will be mapped if it is not already.
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 63d6e8cfb7..11a989a2b9 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -476,5 +476,8 @@ extern int userns_exec_minimal(const struct lxc_conf *conf,
 			       int (*fn_child)(void *), void *fn_child_data);
 extern int userns_exec_mapped_root(const char *path, int path_fd,
 				   const struct lxc_conf *conf);
+extern int userns_exec(unsigned int flags, struct lxc_list *idmap,
+		       int (*fn_child)(void *), void *fn_child_data,
+		       uid_t uid, gid_t gid);
 
 #endif /* __LXC_CONF_H */


More information about the lxc-devel mailing list