source: MondoRescue/branches/3.3/mindi-busybox/util-linux/nsenter.c@ 3625

Last change on this file since 3625 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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