[prev in list] [next in list] [prev in thread] [next in thread]
List: busybox
Subject: Re: [PATCH v7 5/7] nsenter: new applet
From: Denys Vlasenko <vda.linux () googlemail ! com>
Date: 2016-04-01 20:50:30
Message-ID: CAK1hOcOpfQiVvyVdm+0Kqfwhja9OsXP+Z9SP5xS13zwqCpYZxg () mail ! gmail ! com
[Download RAW message or body]
Applied, did not test it yet. Thanks!
On Fri, Mar 18, 2016 at 12:37 PM, Bartosz Golaszewski
<bartekgola@gmail.com> wrote:
> Implement a fully featured (sans selinux part) nsenter applet.
>
> Signed-off-by: Bartosz Golaszewski <bartekgola@gmail.com>
> ---
> 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 <bartekgola@gmail.com>
> + *
> + * 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 <bartekgola@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] <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
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic