Applied, did not test it yet. Thanks!
On Fri, Mar 18, 2016 at 12:37 PM, Bartosz Golaszewski <[email protected]> wrote: > Implement a fully featured (sans selinux part) nsenter applet. > > Signed-off-by: Bartosz Golaszewski <[email protected]> > --- > util-linux/namespace.h | 20 +++ > util-linux/nsenter.c | 351 > +++++++++++++++++++++++++++++++++++++++++++++++++ > util-linux/unshare.c | 12 +- > 3 files changed, 374 insertions(+), 9 deletions(-) > create mode 100644 util-linux/namespace.h > create mode 100644 util-linux/nsenter.c > > diff --git a/util-linux/namespace.h b/util-linux/namespace.h > new file mode 100644 > index 0000000..331bfe6 > --- /dev/null > +++ b/util-linux/namespace.h > @@ -0,0 +1,20 @@ > +/* vi: set sw=4 ts=4: */ > +/* > + * Common namespace code. > + * > + * Copyright (C) 2016 by Bartosz Golaszewski <[email protected]> > + * > + * Licensed under GPLv2 or later, see file LICENSE in this source tree. > + */ > + > +#ifndef BB_NAMESPACE_H > +#define BB_NAMESPACE_H > + > +/* > + * Longest possible path to a procfs file used in namespace utils. Must be > + * able to contain the '/proc/' string, the '/ns/user' string which is the > + * longest namespace name and a 32-bit integer representing the process ID. > + */ > +#define NS_PROC_PATH_MAX (sizeof("/proc//ns/user") + INT_BUF_MAX(pid_t)) > + > +#endif /* BB_NAMESPACE_H */ > diff --git a/util-linux/nsenter.c b/util-linux/nsenter.c > new file mode 100644 > index 0000000..79a28c6 > --- /dev/null > +++ b/util-linux/nsenter.c > @@ -0,0 +1,351 @@ > +/* vi: set sw=4 ts=4: */ > +/* > + * Mini nsenter implementation for busybox. > + * > + * Copyright (C) 2016 by Bartosz Golaszewski <[email protected]> > + * > + * 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] <program> [args...]" > +//usage:#if ENABLE_FEATURE_NSENTER_LONG_OPTS > +//usage:#define nsenter_full_usage "\n\n" > +//usage: "Options:" > +//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 -P, --preserve-credentials do not touch uids or > gids" > +//usage: "\n -r, --root[=<dir>] set the root > directory" > +//usage: "\n -w, --wd[=<dir>] set the working > directory" > +//usage: "\n -F, --no-fork do not fork before > exec'ing <program>" > +//usage:#else > +//usage:#define nsenter_full_usage "\n\n" > +//usage: "Options:" > +//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 -P do not touch uids or gids" > +//usage: "\n -r [<dir>] set the root directory" > +//usage: "\n -w [<dir>] set the working directory" > +//usage: "\n -F do not fork before exec'ing <program>" > +//usage:#endif > + > +#include "libbb.h" > +#include "namespace.h" > + > +#include <sched.h> > + > +enum { > + OPT_target = BIT( 0), > + OPT_mount = BIT( 1), > + OPT_uts = BIT( 2), > + OPT_ipc = BIT( 3), > + OPT_network = BIT( 4), > + OPT_pid = BIT( 5), > + OPT_user = BIT( 6), > + OPT_setuid = BIT( 7), > + OPT_setgid = BIT( 8), > + OPT_prescred = BIT( 9), > + OPT_root = BIT(10), > + OPT_wd = BIT(11), > + OPT_nofork = BIT(12), > +}; > + > +enum { > + NS_USR_POS = 0, > + NS_IPC_POS, > + NS_UTS_POS, > + NS_NET_POS, > + NS_PID_POS, > + NS_MNT_POS, > + NS_COUNT, > +}; > + > +struct namespace_descr { > + const int opt; /* nsenter command-line option */ > + const int flag; /* value passed to setns() */ > + const char *nsfile; /* namespace file in process' procfs entry */ > +}; > + > +struct namespace_ctx { > + char *path; /* optional path to a custom ns file */ > + int fd; /* opened namespace file descriptor */ > +}; > + > +/* > + * Upstream nsenter doesn't support the short option for > --preserve-credentials > + * but let's use -P here in order to let the user use them even with long > + * options disabled in busybox config. > + */ > +static const char opt_str[] = "t:m::u::i::n::p::U::S:G:Pr::w::F"; > + > +#if ENABLE_FEATURE_NSENTER_LONG_OPTS > +static const char nsenter_longopts[] ALIGN1 = > + "target\0" Required_argument "t" > + "mount\0" Optional_argument "m" > + "uts\0" Optional_argument "u" > + "ipc\0" Optional_argument "i" > + "network\0" Optional_argument "n" > + "pid\0" Optional_argument "p" > + "user\0" Optional_argument "U" > + "setuid\0" Required_argument "S" > + "setgid\0" Required_argument "G" > + "preserve-credentials\0" No_argument "P" > + "root\0" Optional_argument "r" > + "wd\0" Optional_argument "w" > + "no-fork\0" No_argument "F"; > +#endif > + > +/* > + * As opposed to unshare, 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 the other > + * namespaces. > + */ > +static const struct namespace_descr ns_list[] = { > + [NS_USR_POS] = { > + .opt = OPT_user, > + .flag = CLONE_NEWUSER, > + .nsfile = "user", > + }, > + [NS_IPC_POS] = { > + .opt = OPT_ipc, > + .flag = CLONE_NEWIPC, > + .nsfile = "ipc", > + }, > + [NS_UTS_POS] = { > + .opt = OPT_uts, > + .flag = CLONE_NEWUTS, > + .nsfile = "uts", > + }, > + [NS_NET_POS] = { > + .opt = OPT_network, > + .flag = CLONE_NEWNET, > + .nsfile = "net", > + }, > + [NS_PID_POS] = { > + .opt = OPT_pid, > + .flag = CLONE_NEWPID, > + .nsfile = "pid", > + }, > + [NS_MNT_POS] = { > + .opt = OPT_mount, > + .flag = CLONE_NEWNS, > + .nsfile = "mnt", > + }, > +}; > + > +/* > + * 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 *fs_path, > + pid_t target_pid, const char *target_file) > +{ > + char proc_path_buf[NS_PROC_PATH_MAX]; > + const char *path; > + > + if (fs_path) { > + path = fs_path; > + } else if (target_pid) { > + snprintf(proc_path_buf, sizeof(proc_path_buf), > + "/proc/%d/%s", target_pid, target_file); > + path = proc_path_buf; > + } else { > + bb_error_msg_and_die( > + "neither filename nor target pid supplied for %s", > + target_file); > + } > + > + 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, root_fd = -1, wd_fd = -1, status, setgroups_failed = 0; > + const char *root_dir_str = NULL, *wd_str = NULL; > + const char *target_pid_str, *uid_str, *gid_str; > + struct namespace_ctx ns_ctx_list[NS_COUNT]; > + pid_t target_pid = 0; > + unsigned int opts; > + uid_t uid = 0; > + gid_t gid = 0; > + > + IF_FEATURE_NSENTER_LONG_OPTS(applet_long_options = nsenter_longopts); > + > + for (i = 0; i < NS_COUNT; i++) { > + ns_ctx_list[i].path = NULL; > + ns_ctx_list[i].fd = -1; > + } > + > + opts = getopt32(argv, opt_str, &target_pid_str, > + &ns_ctx_list[NS_MNT_POS].path, > + &ns_ctx_list[NS_UTS_POS].path, > + &ns_ctx_list[NS_IPC_POS].path, > + &ns_ctx_list[NS_NET_POS].path, > + &ns_ctx_list[NS_PID_POS].path, > + &ns_ctx_list[NS_USR_POS].path, > + &uid_str, &gid_str,&root_dir_str, &wd_str); > + argv += optind; > + > + if (opts & OPT_target) > + target_pid = xstrtoul(target_pid_str, 10); > + > + for (i = 0; i < NS_COUNT; i++) { > + const struct namespace_descr *ns = &ns_list[i]; > + struct namespace_ctx *ns_ctx = &ns_ctx_list[i]; > + char nsfile[sizeof("ns/user")]; > + > + if (opts & ns->opt) { > + snprintf(nsfile, sizeof(nsfile), "ns/%s", ns->nsfile); > + ns_ctx->fd = open_by_path_or_target(ns_ctx->path, > + target_pid, > + nsfile); > + } > + } > + > + 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"); > + > + if (opts & OPT_setgid) > + gid = xstrtoul(gid_str, 10); > + > + if (opts & OPT_setuid) > + uid = xstrtoul(uid_str, 10); > + > + /* > + * Entering the user namespace without --preserve-credentials implies > + * --setuid & --setgid and clearing root's groups. > + */ > + 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. > + */ > + status = setgroups(0, NULL); > + if (status < 0) > + setgroups_failed = 1; > + } > + > + 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; > + > + status = setns(ns_ctx->fd, ns->flag); > + if (status < 0) { > + bb_perror_msg_and_die( > + "reassociate to namespace '%s' failed", > + ns->nsfile); > + } > + > + 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)) { > + int exit_status; > + pid_t pid; > + > + pid = xfork(); > + if (pid > 0) { > + status = safe_waitpid(pid, &exit_status, 0); > + if (status < 0) > + bb_perror_msg_and_die("waitpid"); > + > + if (WIFEXITED(exit_status)) > + return WEXITSTATUS(exit_status); > + else if (WIFSIGNALED(exit_status)) > + kill(getpid(), WTERMSIG(exit_status)); > + > + bb_error_msg_and_die("child exit failed"); > + } /* Child continues. */ > + } > + > + if (opts & OPT_setgid) { > + status = setgroups(0, NULL); > + if (status < 0 && setgroups_failed) > + bb_perror_msg_and_die("setgroups failed"); > + > + xsetgid(gid); > + } > + > + if (opts & OPT_setuid) > + xsetuid(uid); > + > + if (*argv) { > + execvp(*argv, argv); > + bb_perror_msg_and_die("failed to execute %s", *argv); > + } > + > + run_shell(getenv("SHELL"), 0, NULL, NULL); > +} > diff --git a/util-linux/unshare.c b/util-linux/unshare.c > index 742d336..3bb1990 100644 > --- a/util-linux/unshare.c > +++ b/util-linux/unshare.c > @@ -61,18 +61,12 @@ > //usage:#endif > > #include "libbb.h" > +#include "namespace.h" > > #include <sched.h> > #include <sys/types.h> > #include <sys/mount.h> > > -/* > - * Longest possible path to a procfs file used in unshare. Must be able to > - * contain the '/proc/' string, the '/ns/user' string which is the longest > - * namespace name and a 32-bit integer representing the process ID. > - */ > -#define PROC_PATH_MAX (sizeof("/proc//ns/user") + INT_BUF_MAX(pid_t)) > - > #define PATH_PROC_SETGROUPS "/proc/self/setgroups" > #define PATH_PROC_UIDMAP "/proc/self/uid_map" > #define PATH_PROC_GIDMAP "/proc/self/gid_map" > @@ -209,7 +203,7 @@ static unsigned long parse_propagation(const char > *prop_str) > > static ino_t get_mnt_ns_inode_by_pid(pid_t pid) > { > - char path[PROC_PATH_MAX]; > + char path[NS_PROC_PATH_MAX]; > struct stat statbuf; > > snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid); > @@ -222,7 +216,7 @@ static void mount_namespaces(pid_t pid, struct > namespace_ctx *ns_ctx_list) > { > const struct namespace_descr *ns; > struct namespace_ctx *ns_ctx; > - char nsf[PROC_PATH_MAX]; > + char nsf[NS_PROC_PATH_MAX]; > int i, status; > > for (i = 0; i < NS_COUNT; i++) { > -- > 2.1.4 > _______________________________________________ busybox mailing list [email protected] http://lists.busybox.net/mailman/listinfo/busybox
