bc: convert to "G trick" - this returns bc to zero bss increase
[oweals/busybox.git] / util-linux / nsenter.c
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 (8.6 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
58 struct namespace_descr {
59         int flag;               /* value passed to setns() */
60         char ns_nsfile8[8];     /* "ns/" + namespace file in process' procfs entry */
61 };
62
63 struct namespace_ctx {
64         char *path;             /* optional path to a custom ns file */
65         int fd;                 /* opened namespace file descriptor */
66 };
67
68 enum {
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 };
83 enum {
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  */
97 static 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  */
108 static const char opt_str[] ALIGN1 = "U::i::u::n::p::m::""t:+S:+G:+r::w::F";
109
110 #if ENABLE_LONG_OPTS
111 static 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  */
134 static 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
154 int nsenter_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
155 int 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 }