xref: /aosp_15_r20/external/toybox/toys/other/nsenter.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1 /* nsenter.c - Enter existing namespaces
2  *
3  * Copyright 2014 Andy Lutomirski <[email protected]>
4  *
5  * See http://man7.org/linux/man-pages/man1/nsenter.1.html
6  *
7  * unshare.c - run command in new context
8  *
9  * Copyright 2011 Rob Landley <[email protected]>
10  *
11  * See http://man7.org/linux/man-pages/man1/unshare.1.html
12  *
13 
14 // Note: flags go in same order (right to left) for shared subset
15 USE_NSENTER(NEWTOY(nsenter, "<1a(all)F(no-fork)t#<1(target)C(cgroup):; i(ipc):; m(mount):; n(net):; p(pid):; u(uts):; U(user):; ", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
16 USE_UNSHARE(NEWTOY(unshare, "<1^a(all)f(fork)r(map-root-user)C(cgroup):; i(ipc):; m(mount):; n(net):; p(pid):; u(uts):; U(user):; ", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT))
17 
18 config UNSHARE
19   bool "unshare"
20   default y
21   help
22     usage: unshare [-imnpuUr] COMMAND...
23 
24     Create new container namespace(s) for this process and its children, allowing
25     the new set of processes to have a different view of the system than the
26     parent process.
27 
28     -a	Unshare all supported namespaces
29     -f	Fork command in the background (--fork)
30     -r	Become root (map current euid/egid to 0/0, implies -U) (--map-root-user)
31 
32     Available namespaces:
33     -C	Control groups (--cgroup)
34     -i	SysV IPC (message queues, semaphores, shared memory) (--ipc)
35     -m	Mount/unmount tree (--mount)
36     -n	Network address, sockets, routing, iptables (--net)
37     -p	Process IDs and init (--pid)
38     -u	Host and domain names (--uts)
39     -U	UIDs, GIDs, capabilities (--user)
40 
41     Each namespace can take an optional argument, a persistent mountpoint usable
42     by the nsenter command to add new processes to that the namespace. (Specify
43     multiple namespaces to unshare separately, ala -c -i -m because -cim is -c
44     with persistent mount "im".)
45 
46 config NSENTER
47   bool "nsenter"
48   default y
49   help
50     usage: nsenter [-t pid] [-F] [-i] [-m] [-n] [-p] [-u] [-U] COMMAND...
51 
52     Run COMMAND in an existing (set of) namespace(s).
53 
54     -a	Enter all supported namespaces (--all)
55     -F	don't fork, even if -p is used (--no-fork)
56     -t	PID to take namespaces from    (--target)
57 
58     The namespaces to switch are:
59 
60     -C	Control groups (--cgroup)
61     -i	SysV IPC: message queues, semaphores, shared memory (--ipc)
62     -m	Mount/unmount tree (--mount)
63     -n	Network address, sockets, routing, iptables (--net)
64     -p	Process IDs and init, will fork unless -F is used (--pid)
65     -u	Host and domain names (--uts)
66     -U	UIDs, GIDs, capabilities (--user)
67 
68     If -t isn't specified, each namespace argument must provide a path
69     to a namespace file, ala "-i=/proc/$PID/ns/ipc"
70 */
71 
72 #define FOR_nsenter
73 #include "toys.h"
74 #include <linux/sched.h>
75 
76 #define unshare(flags) syscall(SYS_unshare, flags)
77 #define setns(fd, nstype) syscall(SYS_setns, fd, nstype)
78 
GLOBALS(char * UupnmiC[7];long t;)79 GLOBALS(
80   char *UupnmiC[7];
81   long t;
82 )
83 
84 // Code that must run in unshare's flag context
85 #define FOR_unshare
86 #include <generated/flags.h>
87 
88 static void write_ugid_map(char *map, unsigned eugid)
89 {
90   int fd = xopen(map, O_WRONLY);
91 
92   dprintf(fd, "0 %u 1", eugid);
93   xclose(fd);
94 }
95 
test_a()96 static int test_a() { return FLAG(a); }
test_r()97 static int test_r() { return FLAG(r); }
test_f()98 static int test_f() { return FLAG(f); }
99 
100 // Shift back to the context GLOBALS lives in (I.E. matching the filename).
101 #define FOR_nsenter
102 #include <generated/flags.h>
103 
unshare_main(void)104 void unshare_main(void)
105 {
106   char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc\0cgroup";
107   unsigned flags[]={CLONE_NEWUSER, CLONE_NEWUTS, CLONE_NEWPID, CLONE_NEWNET,
108                     CLONE_NEWNS, CLONE_NEWIPC, CLONE_NEWCGROUP}, f = 0;
109   int i, fd;
110 
111   // Create new namespace(s)?
112   if (CFG_UNSHARE && *toys.which->name=='u') {
113     // For -r, we have to save our original [ug]id before calling unshare()
114     int euid = geteuid(), egid = getegid();
115 
116     // unshare -U does not imply -r, so we cannot use [+rU]
117     if (test_r()) toys.optflags |= FLAG_U;
118 
119     for (i = 0; i<ARRAY_LEN(flags); i++)
120       if (test_a() || (toys.optflags & (1<<i))) f |= flags[i];
121     if (unshare(f)) perror_exit(0);
122     if (test_r()) {
123       if ((fd = open("/proc/self/setgroups", O_WRONLY)) >= 0) {
124         xwrite(fd, "deny", 4);
125         close(fd);
126       }
127 
128       write_ugid_map("/proc/self/uid_map", euid);
129       write_ugid_map("/proc/self/gid_map", egid);
130     }
131 
132     if (test_f()) {
133       toys.exitval = xrun(toys.optargs);
134 
135       return;
136     }
137   // Bind to existing namespace(s)?
138   } else if (CFG_NSENTER) {
139     for (i = 0; i<ARRAY_LEN(flags); i++, nsnames += strlen(nsnames)+1) {
140       if (FLAG(a) || (toys.optflags & (1<<i))) {
141         char *filename = TT.UupnmiC[i];
142 
143         if (!filename || !*filename) {
144           if (!FLAG(t)) error_exit("need -t or =filename");
145           sprintf(filename = toybuf, "/proc/%ld/ns/%s", TT.t, nsnames);
146         }
147 
148         if (setns(fd = xopenro(filename), flags[i])) perror_exit("setns");
149         close(fd);
150       }
151     }
152 
153     if (FLAG(p) && !FLAG(F)) {
154       toys.exitval = xrun(toys.optargs);
155 
156       return;
157     }
158   }
159 
160   xexec(toys.optargs);
161 }
162 
nsenter_main(void)163 void nsenter_main(void)
164 {
165   unshare_main();
166 }
167