busybox/util-linux/nsenter.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini nsenter implementation for busybox.
   4 *
   5 * Copyright (C) 2016 by Bartosz Golaszewski <bartekgola@gmail.com>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 */
   9//config:config NSENTER
  10//config:       bool "nsenter (6.5 kb)"
  11//config:       default y
  12//config:       select PLATFORM_LINUX
  13//config:       help
  14//config:       Run program with namespaces of other processes.
  15
  16//applet:IF_NSENTER(APPLET(nsenter, BB_DIR_USR_BIN, BB_SUID_DROP))
  17
  18//kbuild:lib-$(CONFIG_NSENTER) += nsenter.o
  19
  20//usage:#define nsenter_trivial_usage
  21//usage:       "[OPTIONS] [PROG [ARGS]]"
  22//usage:#define nsenter_full_usage "\n"
  23//usage:     "\n        -t PID          Target process to get namespaces from"
  24//usage:     "\n        -m[FILE]        Enter mount namespace"
  25//usage:     "\n        -u[FILE]        Enter UTS namespace (hostname etc)"
  26//usage:     "\n        -i[FILE]        Enter System V IPC namespace"
  27//usage:     "\n        -n[FILE]        Enter network namespace"
  28//usage:     "\n        -p[FILE]        Enter pid namespace"
  29//usage:     "\n        -U[FILE]        Enter user namespace"
  30//usage:     "\n        -S UID          Set uid in entered namespace"
  31//usage:     "\n        -G GID          Set gid in entered namespace"
  32//usage:        IF_LONG_OPTS(
  33//usage:     "\n        --preserve-credentials  Don't touch uids or gids"
  34//usage:        )
  35//usage:     "\n        -r[DIR]         Set root directory"
  36//usage:     "\n        -w[DIR]         Set working directory"
  37//usage:     "\n        -F              Don't fork before exec'ing PROG"
  38
  39#include <sched.h>
  40#ifndef CLONE_NEWUTS
  41# define CLONE_NEWUTS  0x04000000
  42#endif
  43#ifndef CLONE_NEWIPC
  44# define CLONE_NEWIPC  0x08000000
  45#endif
  46#ifndef CLONE_NEWUSER
  47# define CLONE_NEWUSER 0x10000000
  48#endif
  49#ifndef CLONE_NEWPID
  50# define CLONE_NEWPID  0x20000000
  51#endif
  52#ifndef CLONE_NEWNET
  53# define CLONE_NEWNET  0x40000000
  54#endif
  55
  56#include "libbb.h"
  57
  58struct namespace_descr {
  59        int flag;               /* value passed to setns() */
  60        char ns_nsfile8[8];     /* "ns/" + namespace file in process' procfs entry */
  61};
  62
  63struct namespace_ctx {
  64        char *path;             /* optional path to a custom ns file */
  65        int fd;                 /* opened namespace file descriptor */
  66};
  67
  68enum {
  69        OPT_user        = 1 << 0,
  70        OPT_ipc         = 1 << 1,
  71        OPT_uts         = 1 << 2,
  72        OPT_network     = 1 << 3,
  73        OPT_pid         = 1 << 4,
  74        OPT_mount       = 1 << 5,
  75        OPT_target      = 1 << 6,
  76        OPT_setuid      = 1 << 7,
  77        OPT_setgid      = 1 << 8,
  78        OPT_root        = 1 << 9,
  79        OPT_wd          = 1 << 10,
  80        OPT_nofork      = 1 << 11,
  81        OPT_prescred    = (1 << 12) * ENABLE_LONG_OPTS,
  82};
  83enum {
  84        NS_USR_POS = 0,
  85        NS_IPC_POS,
  86        NS_UTS_POS,
  87        NS_NET_POS,
  88        NS_PID_POS,
  89        NS_MNT_POS,
  90        NS_COUNT,
  91};
  92/*
  93 * The order is significant in nsenter.
  94 * The user namespace comes first, so that it is entered first.
  95 * This gives an unprivileged user the potential to enter other namespaces.
  96 */
  97static const struct namespace_descr ns_list[] = {
  98        { CLONE_NEWUSER, "ns/user", },
  99        { CLONE_NEWIPC,  "ns/ipc",  },
 100        { CLONE_NEWUTS,  "ns/uts",  },
 101        { CLONE_NEWNET,  "ns/net",  },
 102        { CLONE_NEWPID,  "ns/pid",  },
 103        { CLONE_NEWNS,   "ns/mnt",  },
 104};
 105/*
 106 * Upstream nsenter doesn't support the short option for --preserve-credentials
 107 */
 108static const char opt_str[] ALIGN1 = "U::i::u::n::p::m::""t:+S:+G:+r::w::F";
 109
 110#if ENABLE_LONG_OPTS
 111static const char nsenter_longopts[] ALIGN1 =
 112        "user\0"                        Optional_argument       "U"
 113        "ipc\0"                         Optional_argument       "i"
 114        "uts\0"                         Optional_argument       "u"
 115        "net\0"                         Optional_argument       "n"
 116        "pid\0"                         Optional_argument       "p"
 117        "mount\0"                       Optional_argument       "m"
 118        "target\0"                      Required_argument       "t"
 119        "setuid\0"                      Required_argument       "S"
 120        "setgid\0"                      Required_argument       "G"
 121        "root\0"                        Optional_argument       "r"
 122        "wd\0"                          Optional_argument       "w"
 123        "no-fork\0"                     No_argument             "F"
 124        "preserve-credentials\0"        No_argument             "\xff"
 125        ;
 126#endif
 127
 128/*
 129 * Open a file and return the new descriptor. If a full path is provided in
 130 * fs_path, then the file to which it points is opened. Otherwise (fd_path is
 131 * NULL) the routine builds a path to a procfs file using the following
 132 * template: '/proc/<target_pid>/<target_file>'.
 133 */
 134static int open_by_path_or_target(const char *path,
 135                                  pid_t target_pid, const char *target_file)
 136{
 137        char proc_path_buf[sizeof("/proc/%u/1234567890") + sizeof(int)*3];
 138
 139        if (!path) {
 140                if (target_pid == 0) {
 141                        /* Example:
 142                         * "nsenter -p PROG" - neither -pFILE nor -tPID given.
 143                         */
 144                        bb_show_usage();
 145                }
 146                snprintf(proc_path_buf, sizeof(proc_path_buf),
 147                         "/proc/%u/%s", (unsigned)target_pid, target_file);
 148                path = proc_path_buf;
 149        }
 150
 151        return xopen(path, O_RDONLY);
 152}
 153
 154int nsenter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 155int nsenter_main(int argc UNUSED_PARAM, char **argv)
 156{
 157        int i;
 158        unsigned int opts;
 159        const char *root_dir_str = NULL;
 160        const char *wd_str = NULL;
 161        struct namespace_ctx ns_ctx_list[NS_COUNT];
 162        int setgroups_failed;
 163        int root_fd, wd_fd;
 164        int target_pid = 0;
 165        int uid = 0;
 166        int gid = 0;
 167
 168        memset(ns_ctx_list, 0, sizeof(ns_ctx_list));
 169
 170        opts = getopt32long(argv, opt_str, nsenter_longopts,
 171                        &ns_ctx_list[NS_USR_POS].path,
 172                        &ns_ctx_list[NS_IPC_POS].path,
 173                        &ns_ctx_list[NS_UTS_POS].path,
 174                        &ns_ctx_list[NS_NET_POS].path,
 175                        &ns_ctx_list[NS_PID_POS].path,
 176                        &ns_ctx_list[NS_MNT_POS].path,
 177                        &target_pid, &uid, &gid,
 178                        &root_dir_str, &wd_str
 179        );
 180        argv += optind;
 181
 182        root_fd = wd_fd = -1;
 183        if (opts & OPT_root)
 184                root_fd = open_by_path_or_target(root_dir_str,
 185                                                 target_pid, "root");
 186        if (opts & OPT_wd)
 187                wd_fd = open_by_path_or_target(wd_str, target_pid, "cwd");
 188
 189        for (i = 0; i < NS_COUNT; i++) {
 190                const struct namespace_descr *ns = &ns_list[i];
 191                struct namespace_ctx *ns_ctx = &ns_ctx_list[i];
 192
 193                ns_ctx->fd = -1;
 194                if (opts & (1 << i))
 195                        ns_ctx->fd = open_by_path_or_target(ns_ctx->path,
 196                                        target_pid, ns->ns_nsfile8);
 197        }
 198
 199        /*
 200         * Entering the user namespace without --preserve-credentials implies
 201         * --setuid & --setgid and clearing root's groups.
 202         */
 203        setgroups_failed = 0;
 204        if ((opts & OPT_user) && !(opts & OPT_prescred)) {
 205                opts |= (OPT_setuid | OPT_setgid);
 206                /*
 207                 * We call setgroups() before and after setns() and only
 208                 * bail-out if it fails twice.
 209                 */
 210                setgroups_failed = (setgroups(0, NULL) < 0);
 211        }
 212
 213        for (i = 0; i < NS_COUNT; i++) {
 214                const struct namespace_descr *ns = &ns_list[i];
 215                struct namespace_ctx *ns_ctx = &ns_ctx_list[i];
 216
 217                if (ns_ctx->fd < 0)
 218                        continue;
 219                if (setns(ns_ctx->fd, ns->flag)) {
 220                        bb_perror_msg_and_die(
 221                                "setns(): can't reassociate to namespace '%s'",
 222                                ns->ns_nsfile8 + 3 /* skip over "ns/" */
 223                        );
 224                }
 225                close(ns_ctx->fd); /* should close fds, to not confuse exec'ed PROG */
 226                /*ns_ctx->fd = -1;*/
 227        }
 228
 229        if (root_fd >= 0) {
 230                if (wd_fd < 0) {
 231                        /*
 232                         * Save the current working directory if we're not
 233                         * changing it.
 234                         */
 235                        wd_fd = xopen(".", O_RDONLY);
 236                }
 237                xfchdir(root_fd);
 238                xchroot(".");
 239                close(root_fd);
 240                /*root_fd = -1;*/
 241        }
 242
 243        if (wd_fd >= 0) {
 244                xfchdir(wd_fd);
 245                close(wd_fd);
 246                /*wd_fd = -1;*/
 247        }
 248
 249        /*
 250         * Entering the pid namespace implies forking unless it's been
 251         * explicitly requested by the user not to.
 252         */
 253        if (!(opts & OPT_nofork) && (opts & OPT_pid)) {
 254                xvfork_parent_waits_and_exits();
 255                /* Child continues */
 256        }
 257
 258        if (opts & OPT_setgid) {
 259                if (setgroups(0, NULL) < 0 && setgroups_failed)
 260                        bb_perror_msg_and_die("setgroups");
 261                xsetgid(gid);
 262        }
 263        if (opts & OPT_setuid)
 264                xsetuid(uid);
 265
 266        exec_prog_or_SHELL(argv);
 267}
 268