[git commit] nsenter: new applet

Denys Vlasenko vda.linux at googlemail.com
Fri Apr 1 20:19:35 UTC 2016


commit: https://git.busybox.net/busybox/commit/?id=80c934a2517f7bfc7642da6555d7ca01bc6f2edd
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
nsenter_main                                           -     663    +663
.rodata                                           155147  155612    +465
packed_usage                                       30536   30708    +172
nsenter_longopts                                       -     116    +116
open_by_path_or_target                                 -      58     +58
applet_names                                        2518    2526      +8
applet_main                                         2920    2928      +8
------------------------------------------------------------------------------
(add/remove: 4/0 grow/shrink: 4/0 up/down: 1490/0)           Total: 1490 bytes
   text	   data	    bss	    dec	    hex	filename
 827956	   4078	   9080	 841114	  cd59a	busybox_old
 829214	   4086	   9080	 842380	  cda8c	busybox_unstripped

Signed-off-by: Bartosz Golaszewski <bartekgola at gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 util-linux/nsenter.c | 286 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 286 insertions(+)

diff --git a/util-linux/nsenter.c b/util-linux/nsenter.c
new file mode 100644
index 0000000..9c1daba
--- /dev/null
+++ b/util-linux/nsenter.c
@@ -0,0 +1,286 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini nsenter implementation for busybox.
+ *
+ * Copyright (C) 2016 by Bartosz Golaszewski <bartekgola at gmail.com>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+
+//config:config NSENTER
+//config:	bool "nsenter"
+//config:	default y
+//config:	select PLATFORM_LINUX
+//config:	help
+//config:	  Run program with namespaces of other processes.
+//config:
+//config:config FEATURE_NSENTER_LONG_OPTS
+//config:	bool "Enable long options"
+//config:	default y
+//config:	depends on NSENTER && LONG_OPTS
+//config:	help
+//config:	  Support long options for the nsenter applet. This makes
+//config:	  the busybox implementation more compatible with upstream.
+
+//applet:IF_NSENTER(APPLET(nsenter, BB_DIR_USR_BIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_NSENTER) += nsenter.o
+
+//usage:#define nsenter_trivial_usage
+//usage:       "[OPTIONS] [PROG [ARGS]]"
+//usage:#if ENABLE_FEATURE_NSENTER_LONG_OPTS
+//usage:#define nsenter_full_usage "\n"
+//usage:     "\n	-t, --target=PID		Target process to get namespaces from"
+//usage:     "\n	-m, --mount[=FILE]		Enter mount namespace"
+//usage:     "\n	-u, --uts[=FILE]		Enter UTS namespace (hostname etc)"
+//usage:     "\n	-i, --ipc[=FILE]		Enter System V IPC namespace"
+//usage:     "\n	-n, --net[=FILE]		Enter network namespace"
+//usage:     "\n	-p, --pid[=FILE]		Enter pid namespace"
+//usage:     "\n	-U, --user[=FILE]		Enter user namespace"
+//usage:     "\n	-S, --setuid=UID		Set uid in entered namespace"
+//usage:     "\n	-G, --setgid=GID		Set gid in entered namespace"
+//usage:     "\n	--preserve-credentials		Don't touch uids or gids"
+//usage:     "\n	-r, --root[=DIR]		Set root directory"
+//usage:     "\n	-w, --wd[=DIR]			Set working directory"
+//usage:     "\n	-F, --no-fork			Don't fork before exec'ing PROG"
+//usage:#else
+//usage:#define nsenter_full_usage "\n"
+//usage:     "\n	-t PID		Target process to get namespaces from"
+//usage:     "\n	-m[FILE]	Enter mount namespace"
+//usage:     "\n	-u[FILE]	Enter UTS namespace (hostname etc)"
+//usage:     "\n	-i[FILE]	Enter System V IPC namespace"
+//usage:     "\n	-n[FILE]	Enter network namespace"
+//usage:     "\n	-p[FILE]	Enter pid namespace"
+//usage:     "\n	-U[FILE]	Enter user namespace"
+//usage:     "\n	-S UID		Set uid in entered namespace"
+//usage:     "\n	-G GID		Set gid in entered namespace"
+//usage:     "\n	-r[DIR]		Set root directory"
+//usage:     "\n	-w[DIR]		Set working directory"
+//usage:     "\n	-F		Don't fork before exec'ing PROG"
+//usage:#endif
+
+#include <sched.h>
+#include "libbb.h"
+
+struct namespace_descr {
+	int flag;		/* value passed to setns() */
+	char ns_nsfile8[8];	/* "ns/" + namespace file in process' procfs entry */
+};
+
+struct namespace_ctx {
+	char *path;		/* optional path to a custom ns file */
+	int fd;			/* opened namespace file descriptor */
+};
+
+enum {
+	OPT_user	= 1 << 0,
+	OPT_ipc		= 1 << 1,
+	OPT_uts		= 1 << 2,
+	OPT_network	= 1 << 3,
+	OPT_pid		= 1 << 4,
+	OPT_mount	= 1 << 5,
+	OPT_target	= 1 << 6,
+	OPT_setuid	= 1 << 7,
+	OPT_setgid	= 1 << 8,
+	OPT_root	= 1 << 9,
+	OPT_wd		= 1 << 10,
+	OPT_nofork	= 1 << 11,
+	OPT_prescred	= (1 << 12) * ENABLE_FEATURE_NSENTER_LONG_OPTS,
+};
+enum {
+	NS_USR_POS = 0,
+	NS_IPC_POS,
+	NS_UTS_POS,
+	NS_NET_POS,
+	NS_PID_POS,
+	NS_MNT_POS,
+	NS_COUNT,
+};
+/*
+ * The order is significant in nsenter.
+ * The user namespace comes first, so that it is entered first.
+ * This gives an unprivileged user the potential to enter other namespaces.
+ */
+static const struct namespace_descr ns_list[] = {
+	{ CLONE_NEWUSER, "ns/user", },
+	{ CLONE_NEWIPC,  "ns/ipc",  },
+	{ CLONE_NEWUTS,  "ns/uts",  },
+	{ CLONE_NEWNET,  "ns/net",  },
+	{ CLONE_NEWPID,  "ns/pid",  },
+	{ CLONE_NEWNS,   "ns/mnt",  },
+};
+/*
+ * Upstream nsenter doesn't support the short option for --preserve-credentials
+ */
+static const char opt_str[] = "U::i::u::n::p::m::""t+S+G+r::w::F";
+
+#if ENABLE_FEATURE_NSENTER_LONG_OPTS
+static const char nsenter_longopts[] ALIGN1 =
+	"user\0"			Optional_argument	"U"
+	"ipc\0"				Optional_argument	"i"
+	"uts\0"				Optional_argument	"u"
+	"network\0"			Optional_argument	"n"
+	"pid\0"				Optional_argument	"p"
+	"mount\0"			Optional_argument	"m"
+	"target\0"			Required_argument	"t"
+	"setuid\0"			Required_argument	"S"
+	"setgid\0"			Required_argument	"G"
+	"root\0"			Optional_argument	"r"
+	"wd\0"				Optional_argument	"w"
+	"no-fork\0"			No_argument		"F"
+	"preserve-credentials\0"	No_argument		"\xff"
+	;
+#endif
+
+/*
+ * Open a file and return the new descriptor. If a full path is provided in
+ * fs_path, then the file to which it points is opened. Otherwise (fd_path is
+ * NULL) the routine builds a path to a procfs file using the following
+ * template: '/proc/<target_pid>/<target_file>'.
+ */
+static int open_by_path_or_target(const char *path,
+				  pid_t target_pid, const char *target_file)
+{
+	char proc_path_buf[sizeof("/proc/%u/1234567890") + sizeof(int)*3];
+
+	if (!path) {
+		if (target_pid == 0) {
+			/* Example:
+			 * "nsenter -p PROG" - neither -pFILE nor -tPID given.
+			 */
+			bb_show_usage();
+		}
+		snprintf(proc_path_buf, sizeof(proc_path_buf),
+			 "/proc/%u/%s", (unsigned)target_pid, target_file);
+		path = proc_path_buf;
+	}
+
+	return xopen(path, O_RDONLY);
+}
+
+int nsenter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int nsenter_main(int argc UNUSED_PARAM, char **argv)
+{
+	int i;
+	unsigned int opts;
+	const char *root_dir_str = NULL;
+	const char *wd_str = NULL;
+	struct namespace_ctx ns_ctx_list[NS_COUNT];
+	int setgroups_failed;
+	int root_fd, wd_fd;
+	int target_pid = 0;
+	int uid = 0;
+	int gid = 0;
+
+	memset(ns_ctx_list, 0, sizeof(ns_ctx_list));
+
+	IF_FEATURE_NSENTER_LONG_OPTS(applet_long_options = nsenter_longopts);
+	opts = getopt32(argv, opt_str,
+			&ns_ctx_list[NS_USR_POS].path,
+			&ns_ctx_list[NS_IPC_POS].path,
+			&ns_ctx_list[NS_UTS_POS].path,
+			&ns_ctx_list[NS_NET_POS].path,
+			&ns_ctx_list[NS_PID_POS].path,
+			&ns_ctx_list[NS_MNT_POS].path,
+			&target_pid, &uid, &gid,
+			&root_dir_str, &wd_str
+	);
+	argv += optind;
+
+	root_fd = wd_fd = -1;
+	if (opts & OPT_root)
+		root_fd = open_by_path_or_target(root_dir_str,
+						 target_pid, "root");
+	if (opts & OPT_wd)
+		wd_fd = open_by_path_or_target(wd_str, target_pid, "cwd");
+
+	for (i = 0; i < NS_COUNT; i++) {
+		const struct namespace_descr *ns = &ns_list[i];
+		struct namespace_ctx *ns_ctx = &ns_ctx_list[i];
+
+		ns_ctx->fd = -1;
+		if (opts & (1 << i))
+			ns_ctx->fd = open_by_path_or_target(ns_ctx->path,
+					target_pid, ns->ns_nsfile8);
+	}
+
+	/*
+	 * Entering the user namespace without --preserve-credentials implies
+	 * --setuid & --setgid and clearing root's groups.
+	 */
+	setgroups_failed = 0;
+	if ((opts & OPT_user) && !(opts & OPT_prescred)) {
+		opts |= (OPT_setuid | OPT_setgid);
+		/*
+		 * We call setgroups() before and after setns() and only
+		 * bail-out if it fails twice.
+		 */
+		setgroups_failed = (setgroups(0, NULL) < 0);
+	}
+
+	for (i = 0; i < NS_COUNT; i++) {
+		const struct namespace_descr *ns = &ns_list[i];
+		struct namespace_ctx *ns_ctx = &ns_ctx_list[i];
+
+		if (ns_ctx->fd < 0)
+			continue;
+		if (setns(ns_ctx->fd, ns->flag)) {
+			bb_perror_msg_and_die(
+				"setns(): can't reassociate to namespace '%s'",
+				ns->ns_nsfile8 + 3 /* skip over "ns/" */
+			);
+		}
+		/*close(ns_ctx->fd);*/
+		/*ns_ctx->fd = -1;*/
+	}
+
+	if (root_fd >= 0) {
+		if (wd_fd < 0) {
+			/*
+			 * Save the current working directory if we're not
+			 * changing it.
+			 */
+			wd_fd = xopen(".", O_RDONLY);
+		}
+		xfchdir(root_fd);
+		xchroot(".");
+		/*close(root_fd);*/
+		/*root_fd = -1;*/
+	}
+
+	if (wd_fd >= 0) {
+		xfchdir(wd_fd);
+		/*close(wd_fd);*/
+		/*wd_fd = -1;*/
+	}
+
+	/*
+	 * Entering the pid namespace implies forking unless it's been
+	 * explicitly requested by the user not to.
+	 */
+	if (!(opts & OPT_nofork) && (opts & OPT_pid)) {
+		pid_t pid = xvfork();
+		if (pid > 0) {
+			/* Parent */
+			int exit_status = wait_for_exitstatus(pid);
+			if (WIFSIGNALED(exit_status))
+				kill_myself_with_sig(WTERMSIG(exit_status));
+			return WEXITSTATUS(exit_status);
+		}
+		/* Child continues */
+	}
+
+	if (opts & OPT_setgid) {
+		if (setgroups(0, NULL) < 0 && setgroups_failed)
+			bb_perror_msg_and_die("setgroups");
+		xsetgid(gid);
+	}
+	if (opts & OPT_setuid)
+		xsetuid(uid);
+
+	if (*argv) {
+		BB_EXECVP_or_die(argv);
+	}
+
+	run_shell(getenv("SHELL"), /*login:*/ 1, NULL, NULL);
+}


More information about the busybox-cvs mailing list