]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/nsenter.c
sys-utils: cleanup license lines, add SPDX
[thirdparty/util-linux.git] / sys-utils / nsenter.c
CommitLineData
f8aa8e94 1/*
9abd5e4b 2 * SPDX-License-Identifier: GPL-2.0
f8aa8e94
EB
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; version 2.
7 *
9abd5e4b 8 * Copyright (C) 2012-2023 Eric Biederman <ebiederm@xmission.com>
f8aa8e94 9 *
9abd5e4b 10 * nsenter(1) - command-line interface for setns(2)
f8aa8e94 11 */
f8aa8e94
EB
12#include <dirent.h>
13#include <errno.h>
14#include <getopt.h>
15#include <sched.h>
16#include <stdio.h>
17#include <stdlib.h>
984e1b7c 18#include <stdbool.h>
f8aa8e94 19#include <unistd.h>
dfd8b117 20#include <assert.h>
57580694
ZJS
21#include <sys/types.h>
22#include <sys/wait.h>
99d7e174 23#include <grp.h>
974cc006 24#include <sys/stat.h>
b40650b7 25#include <sys/statfs.h>
f8aa8e94 26
01a6d803 27#include <sys/ioctl.h>
28#ifdef HAVE_LINUX_NSFS_H
29# include <linux/nsfs.h>
30#endif
31#ifndef NS_GET_USERNS
32# define NS_GET_USERNS _IO(0xb7, 0x1)
33#endif
34
355ee3b8
KZ
35#ifdef HAVE_LIBSELINUX
36# include <selinux/selinux.h>
37#endif
38
0d3ec860 39#include "strutils.h"
f8aa8e94
EB
40#include "nls.h"
41#include "c.h"
42#include "closestream.h"
c91280a4 43#include "namespace.h"
57580694 44#include "exec_shell.h"
0cbb001a 45#include "optutils.h"
4e9ec856 46#include "xalloc.h"
47#include "all-io.h"
48#include "env.h"
5b175fa9 49#include "caputils.h"
b40650b7 50#include "statfs_magic.h"
51#include "pathnames.h"
f8aa8e94 52
a167328a 53static struct namespace_file {
f8aa8e94 54 int nstype;
f9bbdea6 55 const char *name;
f8aa8e94
EB
56 int fd;
57} namespace_files[] = {
ebbc87cd 58 /* Careful the order is significant in this array.
f8aa8e94 59 *
854d0fef
JB
60 * The user namespace comes either first or last: first if
61 * you're using it to increase your privilege and last if
62 * you're using it to decrease. We enter the namespaces in
63 * two passes starting initially from offset 1 and then offset
64 * 0 if that fails.
f8aa8e94 65 */
f9e7b66d
SH
66 { .nstype = CLONE_NEWUSER, .name = "ns/user", .fd = -1 },
67 { .nstype = CLONE_NEWCGROUP,.name = "ns/cgroup", .fd = -1 },
68 { .nstype = CLONE_NEWIPC, .name = "ns/ipc", .fd = -1 },
69 { .nstype = CLONE_NEWUTS, .name = "ns/uts", .fd = -1 },
70 { .nstype = CLONE_NEWNET, .name = "ns/net", .fd = -1 },
71 { .nstype = CLONE_NEWPID, .name = "ns/pid", .fd = -1 },
72 { .nstype = CLONE_NEWNS, .name = "ns/mnt", .fd = -1 },
7f1f0584 73 { .nstype = CLONE_NEWTIME, .name = "ns/time", .fd = -1 },
9905912f 74 { .nstype = 0, .name = NULL, .fd = -1 }
f8aa8e94
EB
75};
76
fa2cd89a 77static void __attribute__((__noreturn__)) usage(void)
f8aa8e94 78{
fa2cd89a 79 FILE *out = stdout;
f8aa8e94
EB
80
81 fputs(USAGE_HEADER, out);
0f0b5823 82 fprintf(out, _(" %s [options] [<program> [<argument>...]]\n"),
f8aa8e94
EB
83 program_invocation_short_name);
84
451dbcfa
BS
85 fputs(USAGE_SEPARATOR, out);
86 fputs(_("Run a program with namespaces of other processes.\n"), out);
87
f8aa8e94 88 fputs(USAGE_OPTIONS, out);
974cc006 89 fputs(_(" -a, --all enter all namespaces\n"), out);
26f879ed 90 fputs(_(" -t, --target <pid> target process to get namespaces from\n"), out);
42f00a7d
BS
91 fputs(_(" -m, --mount[=<file>] enter mount namespace\n"), out);
92 fputs(_(" -u, --uts[=<file>] enter UTS namespace (hostname etc)\n"), out);
93 fputs(_(" -i, --ipc[=<file>] enter System V IPC namespace\n"), out);
94 fputs(_(" -n, --net[=<file>] enter network namespace\n"), out);
95 fputs(_(" -p, --pid[=<file>] enter pid namespace\n"), out);
f9e7b66d 96 fputs(_(" -C, --cgroup[=<file>] enter cgroup namespace\n"), out);
42f00a7d 97 fputs(_(" -U, --user[=<file>] enter user namespace\n"), out);
01a6d803 98 fputs(_(" --user-parent enter parent user namespace\n"), out);
7f1f0584 99 fputs(_(" -T, --time[=<file>] enter time namespace\n"), out);
ab8b10b2
KZ
100 fputs(_(" -S, --setuid[=<uid>] set uid in entered namespace\n"), out);
101 fputs(_(" -G, --setgid[=<gid>] set gid in entered namespace\n"), out);
e99a6626 102 fputs(_(" --preserve-credentials do not touch uids or gids\n"), out);
5b175fa9 103 fputs(_(" --keep-caps retain capabilities granted in user namespaces\n"), out);
42f00a7d
BS
104 fputs(_(" -r, --root[=<dir>] set the root directory\n"), out);
105 fputs(_(" -w, --wd[=<dir>] set the working directory\n"), out);
4e9ec856 106 fputs(_(" -W, --wdns <dir> set the working directory in namespace\n"), out);
107 fputs(_(" -e, --env inherit environment variables from target process\n"), out);
26f879ed 108 fputs(_(" -F, --no-fork do not fork before exec'ing <program>\n"), out);
b40650b7 109 fputs(_(" -c, --join-cgroup join the cgroup of the target process\n"), out);
355ee3b8
KZ
110#ifdef HAVE_LIBSELINUX
111 fputs(_(" -Z, --follow-context set SELinux context according to --target PID\n"), out);
112#endif
26f879ed 113
f8aa8e94 114 fputs(USAGE_SEPARATOR, out);
bad4c729
MY
115 fprintf(out, USAGE_HELP_OPTIONS(24));
116 fprintf(out, USAGE_MAN_TAIL("nsenter(1)"));
f8aa8e94 117
fa2cd89a 118 exit(EXIT_SUCCESS);
f8aa8e94
EB
119}
120
121static pid_t namespace_target_pid = 0;
122static int root_fd = -1;
123static int wd_fd = -1;
4e9ec856 124static int env_fd = -1;
597ae346 125static int uid_gid_fd = -1;
b40650b7 126static int cgroup_procs_fd = -1;
f8aa8e94 127
01a6d803 128static void set_parent_user_ns_fd(void)
129{
130 struct namespace_file *nsfile = NULL;
131 struct namespace_file *user_nsfile = NULL;
132 int parent_ns = -1;
133
134 for (nsfile = namespace_files; nsfile->nstype; nsfile++) {
135 if (nsfile->nstype == CLONE_NEWUSER)
136 user_nsfile = nsfile;
137
138 if (nsfile->fd == -1)
139 continue;
140
141 parent_ns = ioctl(nsfile->fd, NS_GET_USERNS);
142 if (parent_ns < 0)
143 err(EXIT_FAILURE, _("failed to open parent ns of %s"), nsfile->name);
144
145 break;
146 }
147
148 if (parent_ns < 0)
149 errx(EXIT_FAILURE, _("no namespaces to get parent of"));
32c78705
KZ
150 if (user_nsfile)
151 user_nsfile->fd = parent_ns;
01a6d803 152}
153
154
f9bbdea6 155static void open_target_fd(int *fd, const char *type, const char *path)
f8aa8e94
EB
156{
157 char pathbuf[PATH_MAX];
158
159 if (!path && namespace_target_pid) {
160 snprintf(pathbuf, sizeof(pathbuf), "/proc/%u/%s",
a167328a 161 namespace_target_pid, type);
f8aa8e94
EB
162 path = pathbuf;
163 }
164 if (!path)
a167328a
SK
165 errx(EXIT_FAILURE,
166 _("neither filename nor target pid supplied for %s"),
167 type);
f8aa8e94
EB
168
169 if (*fd >= 0)
170 close(*fd);
171
172 *fd = open(path, O_RDONLY);
173 if (*fd < 0)
8b7a7750 174 err(EXIT_FAILURE, _("cannot open %s"), path);
f8aa8e94
EB
175}
176
f9bbdea6 177static void open_namespace_fd(int nstype, const char *path)
f8aa8e94
EB
178{
179 struct namespace_file *nsfile;
180
181 for (nsfile = namespace_files; nsfile->nstype; nsfile++) {
182 if (nstype != nsfile->nstype)
183 continue;
184
185 open_target_fd(&nsfile->fd, nsfile->name, path);
186 return;
187 }
188 /* This should never happen */
dfd8b117 189 assert(nsfile->nstype);
f8aa8e94
EB
190}
191
974cc006
KZ
192static int get_ns_ino(const char *path, ino_t *ino)
193{
194 struct stat st;
195
196 if (stat(path, &st) != 0)
197 return -errno;
198 *ino = st.st_ino;
199 return 0;
200}
201
b40650b7 202static void open_cgroup_procs(void)
203{
7ceb5406
KZ
204 char *buf = NULL, *path = NULL, *p;
205 int cgroup_fd = 0;
b40650b7 206 char fdpath[PATH_MAX];
207
208 open_target_fd(&cgroup_fd, "cgroup", optarg);
209
210 if (read_all_alloc(cgroup_fd, &buf) < 1)
211 err(EXIT_FAILURE, _("failed to get cgroup path"));
212
7ceb5406
KZ
213 p = strtok(buf, "\n");
214 if (p)
215 path = strrchr(p, ':');
b40650b7 216 if (!path)
217 err(EXIT_FAILURE, _("failed to get cgroup path"));
218 path++;
219
220 snprintf(fdpath, sizeof(fdpath), _PATH_SYS_CGROUP "/%s/cgroup.procs", path);
c67a2b84 221
b40650b7 222 if ((cgroup_procs_fd = open(fdpath, O_WRONLY | O_APPEND)) < 0)
223 err(EXIT_FAILURE, _("failed to open cgroup.procs"));
c67a2b84
KZ
224
225 free(buf);
b40650b7 226}
227
228static int is_cgroup2(void)
229{
230 struct statfs fs_stat;
231 int rc;
232
233 rc = statfs(_PATH_SYS_CGROUP, &fs_stat);
234 if (rc)
235 err(EXIT_FAILURE, _("statfs %s failed"), _PATH_SYS_CGROUP);
236 return F_TYPE_EQUAL(fs_stat.f_type, STATFS_CGROUP2_MAGIC);
237}
238
239static void join_into_cgroup(void)
240{
241 pid_t pid;
242 char buf[ sizeof(stringify_value(UINT32_MAX)) ];
243 int len;
244
245 pid = getpid();
246 len = snprintf(buf, sizeof(buf), "%zu", (size_t) pid);
247 if (write_all(cgroup_procs_fd, buf, len))
248 err(EXIT_FAILURE, _("write cgroup.procs failed"));
249}
250
2f628e8f 251static int is_usable_namespace(pid_t target, const struct namespace_file *nsfile)
974cc006
KZ
252{
253 char path[PATH_MAX];
2f628e8f
YG
254 ino_t my_ino = 0;
255 int rc;
974cc006 256
2f628e8f
YG
257 /* Check NS accessibility */
258 snprintf(path, sizeof(path), "/proc/%u/%s", getpid(), nsfile->name);
259 rc = get_ns_ino(path, &my_ino);
260 if (rc == -ENOENT)
261 return false; /* Unsupported NS */
974cc006 262
2f628e8f
YG
263 /* It is not permitted to use setns(2) to reenter the caller's
264 * current user namespace; see setns(2) man page for more details.
265 */
266 if (nsfile->nstype & CLONE_NEWUSER) {
267 ino_t target_ino = 0;
268
269 snprintf(path, sizeof(path), "/proc/%u/%s", target, nsfile->name);
270 if (get_ns_ino(path, &target_ino) != 0)
271 err(EXIT_FAILURE, _("stat of %s failed"), path);
272
273 if (my_ino == target_ino)
274 return false;
275 }
974cc006 276
2f628e8f 277 return true; /* All pass */
974cc006
KZ
278}
279
c9515f86
EB
280static void continue_as_child(void)
281{
10d7224f 282 pid_t child;
c9515f86
EB
283 int status;
284 pid_t ret;
285
10d7224f
KZ
286 /* Clear any inherited settings */
287 signal(SIGCHLD, SIG_DFL);
288
289 child = fork();
c9515f86
EB
290 if (child < 0)
291 err(EXIT_FAILURE, _("fork failed"));
292
293 /* Only the child returns */
294 if (child == 0)
295 return;
296
297 for (;;) {
298 ret = waitpid(child, &status, WUNTRACED);
299 if ((ret == child) && (WIFSTOPPED(status))) {
300 /* The child suspended so suspend us as well */
301 kill(getpid(), SIGSTOP);
302 kill(child, SIGCONT);
303 } else {
304 break;
305 }
306 }
307 /* Return the child's exit code if possible */
308 if (WIFEXITED(status)) {
309 exit(WEXITSTATUS(status));
a167328a 310 } else if (WIFSIGNALED(status)) {
c9515f86
EB
311 kill(getpid(), WTERMSIG(status));
312 }
313 exit(EXIT_FAILURE);
314}
315
f8aa8e94
EB
316int main(int argc, char *argv[])
317{
e99a6626 318 enum {
5b175fa9
DG
319 OPT_PRESERVE_CRED = CHAR_MAX + 1,
320 OPT_KEEPCAPS,
01a6d803 321 OPT_USER_PARENT,
e99a6626 322 };
f8aa8e94 323 static const struct option longopts[] = {
974cc006 324 { "all", no_argument, NULL, 'a' },
f8aa8e94
EB
325 { "help", no_argument, NULL, 'h' },
326 { "version", no_argument, NULL, 'V'},
327 { "target", required_argument, NULL, 't' },
328 { "mount", optional_argument, NULL, 'm' },
329 { "uts", optional_argument, NULL, 'u' },
330 { "ipc", optional_argument, NULL, 'i' },
331 { "net", optional_argument, NULL, 'n' },
332 { "pid", optional_argument, NULL, 'p' },
333 { "user", optional_argument, NULL, 'U' },
f9e7b66d 334 { "cgroup", optional_argument, NULL, 'C' },
7f1f0584 335 { "time", optional_argument, NULL, 'T' },
97bb98ef
TW
336 { "setuid", required_argument, NULL, 'S' },
337 { "setgid", required_argument, NULL, 'G' },
f8aa8e94
EB
338 { "root", optional_argument, NULL, 'r' },
339 { "wd", optional_argument, NULL, 'w' },
0cbb001a 340 { "wdns", optional_argument, NULL, 'W' },
4e9ec856 341 { "env", no_argument, NULL, 'e' },
28384adc 342 { "no-fork", no_argument, NULL, 'F' },
b40650b7 343 { "join-cgroup", no_argument, NULL, 'c'},
e99a6626 344 { "preserve-credentials", no_argument, NULL, OPT_PRESERVE_CRED },
5b175fa9 345 { "keep-caps", no_argument, NULL, OPT_KEEPCAPS },
01a6d803 346 { "user-parent", no_argument, NULL, OPT_USER_PARENT},
355ee3b8
KZ
347#ifdef HAVE_LIBSELINUX
348 { "follow-context", no_argument, NULL, 'Z' },
349#endif
f8aa8e94
EB
350 { NULL, 0, NULL, 0 }
351 };
0cbb001a
KZ
352 static const ul_excl_t excl[] = { /* rows and cols in ASCII order */
353 { 'W', 'w' },
354 { 0 }
355 };
356 int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
f8aa8e94
EB
357
358 struct namespace_file *nsfile;
854d0fef 359 int c, pass, namespaces = 0, setgroups_nerrs = 0, preserve_cred = 0;
ab8b10b2 360 bool do_rd = false, do_wd = false, do_uid = false, force_uid = false,
01a6d803 361 do_gid = false, force_gid = false, do_env = false, do_all = false,
b40650b7 362 do_join_cgroup = false, do_user_parent = false;
57dbcf94 363 int do_fork = -1; /* unknown yet */
0cbb001a 364 char *wdns = NULL;
6b9e5bf6
RW
365 uid_t uid = 0;
366 gid_t gid = 0;
5b175fa9 367 int keepcaps = 0;
4e9ec856 368 struct ul_env_list *envls;
355ee3b8
KZ
369#ifdef HAVE_LIBSELINUX
370 bool selinux = 0;
371#endif
f8aa8e94 372
999ac5e2 373 setlocale(LC_ALL, "");
f8aa8e94
EB
374 bindtextdomain(PACKAGE, LOCALEDIR);
375 textdomain(PACKAGE);
2c308875 376 close_stdout_atexit();
f8aa8e94 377
28384adc 378 while ((c =
b40650b7 379 getopt_long(argc, argv, "+ahVt:m::u::i::n::p::C::U::T::S:G:r::w::W::ecFZ",
28384adc 380 longopts, NULL)) != -1) {
0cbb001a
KZ
381
382 err_exclusive_options(c, longopts, excl, excl_st);
383
28384adc 384 switch (c) {
974cc006
KZ
385 case 'a':
386 do_all = true;
387 break;
f8aa8e94 388 case 't':
a167328a
SK
389 namespace_target_pid =
390 strtoul_or_err(optarg, _("failed to parse pid"));
f8aa8e94
EB
391 break;
392 case 'm':
984e1b7c
ZJS
393 if (optarg)
394 open_namespace_fd(CLONE_NEWNS, optarg);
395 else
396 namespaces |= CLONE_NEWNS;
f8aa8e94
EB
397 break;
398 case 'u':
984e1b7c
ZJS
399 if (optarg)
400 open_namespace_fd(CLONE_NEWUTS, optarg);
401 else
402 namespaces |= CLONE_NEWUTS;
f8aa8e94
EB
403 break;
404 case 'i':
984e1b7c
ZJS
405 if (optarg)
406 open_namespace_fd(CLONE_NEWIPC, optarg);
407 else
408 namespaces |= CLONE_NEWIPC;
f8aa8e94
EB
409 break;
410 case 'n':
984e1b7c
ZJS
411 if (optarg)
412 open_namespace_fd(CLONE_NEWNET, optarg);
413 else
414 namespaces |= CLONE_NEWNET;
f8aa8e94
EB
415 break;
416 case 'p':
984e1b7c
ZJS
417 if (optarg)
418 open_namespace_fd(CLONE_NEWPID, optarg);
419 else
420 namespaces |= CLONE_NEWPID;
f8aa8e94 421 break;
f9e7b66d
SH
422 case 'C':
423 if (optarg)
424 open_namespace_fd(CLONE_NEWCGROUP, optarg);
425 else
426 namespaces |= CLONE_NEWCGROUP;
427 break;
f8aa8e94 428 case 'U':
984e1b7c
ZJS
429 if (optarg)
430 open_namespace_fd(CLONE_NEWUSER, optarg);
431 else
432 namespaces |= CLONE_NEWUSER;
f8aa8e94 433 break;
7f1f0584
AR
434 case 'T':
435 if (optarg)
436 open_namespace_fd(CLONE_NEWTIME, optarg);
437 else
438 namespaces |= CLONE_NEWTIME;
439 break;
6b9e5bf6 440 case 'S':
97bb98ef 441 if (strcmp(optarg, "follow") == 0)
597ae346 442 do_uid = true;
97bb98ef
TW
443 else
444 uid = strtoul_or_err(optarg, _("failed to parse uid"));
47f42c1d 445 force_uid = true;
6b9e5bf6
RW
446 break;
447 case 'G':
97bb98ef 448 if (strcmp(optarg, "follow") == 0)
597ae346 449 do_gid = true;
97bb98ef
TW
450 else
451 gid = strtoul_or_err(optarg, _("failed to parse gid"));
47f42c1d 452 force_gid = true;
6b9e5bf6 453 break;
28384adc 454 case 'F':
57dbcf94 455 do_fork = 0;
f8aa8e94 456 break;
b40650b7 457 case 'c':
458 do_join_cgroup = true;
459 break;
f8aa8e94 460 case 'r':
984e1b7c 461 if (optarg)
82524a13 462 open_target_fd(&root_fd, "root", optarg);
984e1b7c
ZJS
463 else
464 do_rd = true;
f8aa8e94
EB
465 break;
466 case 'w':
984e1b7c 467 if (optarg)
82524a13 468 open_target_fd(&wd_fd, "cwd", optarg);
984e1b7c
ZJS
469 else
470 do_wd = true;
f8aa8e94 471 break;
0cbb001a
KZ
472 case 'W':
473 wdns = optarg;
474 break;
4e9ec856 475 case 'e':
476 do_env = true;
477 break;
e99a6626
KZ
478 case OPT_PRESERVE_CRED:
479 preserve_cred = 1;
480 break;
5b175fa9
DG
481 case OPT_KEEPCAPS:
482 keepcaps = 1;
483 break;
01a6d803 484 case OPT_USER_PARENT:
485 do_user_parent = true;
486 break;
355ee3b8
KZ
487#ifdef HAVE_LIBSELINUX
488 case 'Z':
489 selinux = 1;
490 break;
491#endif
2c308875
KZ
492 case 'h':
493 usage();
494 case 'V':
495 print_version(EXIT_SUCCESS);
f8aa8e94 496 default:
677ec86c 497 errtryhelp(EXIT_FAILURE);
f8aa8e94
EB
498 }
499 }
500
355ee3b8
KZ
501#ifdef HAVE_LIBSELINUX
502 if (selinux && is_selinux_enabled() > 0) {
503 char *scon = NULL;
504
505 if (!namespace_target_pid)
506 errx(EXIT_FAILURE, _("no target PID specified for --follow-context"));
507 if (getpidcon(namespace_target_pid, &scon) < 0)
508 errx(EXIT_FAILURE, _("failed to get %d SELinux context"),
509 (int) namespace_target_pid);
510 if (setexeccon(scon) < 0)
511 errx(EXIT_FAILURE, _("failed to set exec context to '%s'"), scon);
512 freecon(scon);
513 }
514#endif
974cc006
KZ
515
516 if (do_all) {
517 if (!namespace_target_pid)
518 errx(EXIT_FAILURE, _("no target PID specified for --all"));
519 for (nsfile = namespace_files; nsfile->nstype; nsfile++) {
520 if (nsfile->fd >= 0)
521 continue; /* namespace already specified */
522
2f628e8f 523 if (!is_usable_namespace(namespace_target_pid, nsfile))
974cc006
KZ
524 continue;
525
526 namespaces |= nsfile->nstype;
527 }
528 }
529
984e1b7c
ZJS
530 /*
531 * Open remaining namespace and directory descriptors.
532 */
533 for (nsfile = namespace_files; nsfile->nstype; nsfile++)
534 if (nsfile->nstype & namespaces)
535 open_namespace_fd(nsfile->nstype, NULL);
536 if (do_rd)
537 open_target_fd(&root_fd, "root", NULL);
538 if (do_wd)
539 open_target_fd(&wd_fd, "cwd", NULL);
4e9ec856 540 if (do_env)
541 open_target_fd(&env_fd, "environ", NULL);
597ae346
TW
542 if (do_uid || do_gid)
543 open_target_fd(&uid_gid_fd, "", NULL);
b40650b7 544 if (do_join_cgroup) {
545 if (!is_cgroup2())
546 errx(EXIT_FAILURE, _("--join-cgroup is only supported in cgroup v2"));
547 open_cgroup_procs();
548 }
984e1b7c 549
01a6d803 550 /*
551 * Get parent userns from any available ns.
552 */
553 if (do_user_parent)
554 set_parent_user_ns_fd();
555
83fb8aa5
KZ
556 /*
557 * Update namespaces variable to contain all requested namespaces
558 */
559 for (nsfile = namespace_files; nsfile->nstype; nsfile++) {
560 if (nsfile->fd < 0)
561 continue;
562 namespaces |= nsfile->nstype;
563 }
564
e99a6626
KZ
565 /* for user namespaces we always set UID and GID (default is 0)
566 * and clear root's groups if --preserve-credentials is no specified */
567 if ((namespaces & CLONE_NEWUSER) && !preserve_cred) {
568 force_uid = true, force_gid = true;
569
570 /* We call setgroups() before and after we enter user namespace,
571 * let's complain only if both fail */
572 if (setgroups(0, NULL) != 0)
573 setgroups_nerrs++;
574 }
575
f8aa8e94 576 /*
854d0fef
JB
577 * Now that we know which namespaces we want to enter, enter
578 * them. Do this in two passes, not entering the user
579 * namespace on the first pass. So if we're deprivileging the
580 * container we'll enter the user namespace last and if we're
9e930041 581 * privileging it then we enter the user namespace first
854d0fef 582 * (because the initial setns will fail).
f8aa8e94 583 */
854d0fef
JB
584 for (pass = 0; pass < 2; pass ++) {
585 for (nsfile = namespace_files + 1 - pass; nsfile->nstype; nsfile++) {
586 if (nsfile->fd < 0)
587 continue;
588 if (nsfile->nstype == CLONE_NEWPID && do_fork == -1)
589 do_fork = 1;
590 if (setns(nsfile->fd, nsfile->nstype)) {
591 if (pass != 0)
592 err(EXIT_FAILURE,
593 _("reassociate to namespace '%s' failed"),
594 nsfile->name);
595 else
596 continue;
597 }
598
599 close(nsfile->fd);
600 nsfile->fd = -1;
601 }
f8aa8e94
EB
602 }
603
604 /* Remember the current working directory if I'm not changing it */
0cbb001a 605 if (root_fd >= 0 && wd_fd < 0 && wdns == NULL) {
f8aa8e94
EB
606 wd_fd = open(".", O_RDONLY);
607 if (wd_fd < 0)
a167328a
SK
608 err(EXIT_FAILURE,
609 _("cannot open current working directory"));
f8aa8e94
EB
610 }
611
612 /* Change the root directory */
613 if (root_fd >= 0) {
614 if (fchdir(root_fd) < 0)
a167328a
SK
615 err(EXIT_FAILURE,
616 _("change directory by root file descriptor failed"));
f8aa8e94
EB
617
618 if (chroot(".") < 0)
619 err(EXIT_FAILURE, _("chroot failed"));
12558a4c
SK
620 if (chdir("/"))
621 err(EXIT_FAILURE, _("cannot change directory to %s"), "/");
f8aa8e94
EB
622
623 close(root_fd);
624 root_fd = -1;
625 }
626
0cbb001a
KZ
627 /* working directory specified as in-namespace path */
628 if (wdns) {
629 wd_fd = open(wdns, O_RDONLY);
630 if (wd_fd < 0)
631 err(EXIT_FAILURE,
632 _("cannot open current working directory"));
633 }
634
f8aa8e94
EB
635 /* Change the working directory */
636 if (wd_fd >= 0) {
637 if (fchdir(wd_fd) < 0)
a167328a
SK
638 err(EXIT_FAILURE,
639 _("change directory by working directory file descriptor failed"));
f8aa8e94
EB
640
641 close(wd_fd);
642 wd_fd = -1;
643 }
644
4e9ec856 645 /* Pass environment variables of the target process to the spawned process */
646 if (env_fd >= 0) {
647 if ((envls = env_from_fd(env_fd)) == NULL)
648 err(EXIT_FAILURE, _("failed to get environment variables"));
649 clearenv();
650 if (env_list_setenv(envls) < 0)
651 err(EXIT_FAILURE, _("failed to set environment variables"));
652 env_list_free(envls);
653 close(env_fd);
654 }
655
b40650b7 656 // Join into the target cgroup
657 if (cgroup_procs_fd >= 0)
658 join_into_cgroup();
659
597ae346
TW
660 if (uid_gid_fd >= 0) {
661 struct stat st;
662
663 if (fstat(uid_gid_fd, &st) > 0)
664 err(EXIT_FAILURE, _("can not get process stat"));
665
666 close(uid_gid_fd);
667 uid_gid_fd = -1;
668
669 if (do_uid)
670 uid = st.st_uid;
671 if (do_gid)
672 gid = st.st_gid;
673 }
674
57dbcf94 675 if (do_fork == 1)
c9515f86 676 continue_as_child();
f8aa8e94 677
47f42c1d 678 if (force_uid || force_gid) {
e99a6626 679 if (force_gid && setgroups(0, NULL) != 0 && setgroups_nerrs) /* drop supplementary groups */
99d7e174 680 err(EXIT_FAILURE, _("setgroups failed"));
47f42c1d 681 if (force_gid && setgid(gid) < 0) /* change GID */
6b9e5bf6 682 err(EXIT_FAILURE, _("setgid failed"));
47f42c1d 683 if (force_uid && setuid(uid) < 0) /* change UID */
99d7e174 684 err(EXIT_FAILURE, _("setuid failed"));
6b9e5bf6
RW
685 }
686
5b175fa9
DG
687 if (keepcaps && (namespaces & CLONE_NEWUSER))
688 cap_permitted_to_ambient();
689
57580694
ZJS
690 if (optind < argc) {
691 execvp(argv[optind], argv + optind);
fd777151 692 errexec(argv[optind]);
57580694
ZJS
693 }
694 exec_shell();
f8aa8e94 695}