]>
git.wh0rd.org - home.git/blob - vunshare.c
2 * Written by Mike Frysinger <vapier@gmail.com>
3 * Released into the public domain.
7 * - Make pidns init optional.
8 * - Make setproctitle nicer and include program argv[0].
9 * - Set up prctl(PR_SET_PDEATHSIG).
10 * - Set up prctl(PR_SET_CHILD_SUBREAPER).
29 #include <sys/ioctl.h>
30 #include <sys/mount.h>
31 #include <sys/prctl.h>
32 #include <sys/socket.h>
33 #include <sys/types.h>
36 #define PROG "vunshare"
38 static bool vunshare(int flags
)
40 if (unshare(flags
) == -1) {
42 err(1, "unshare failed");
48 static void unshare_net(void)
50 if (!vunshare(CLONE_NEWNET
))
56 sock
= socket(AF_LOCAL
, SOCK_DGRAM
|SOCK_CLOEXEC
, 0);
58 err(1, "socket(AF_LOCAL) failed");
60 /* Equiv of `ip link set up lo`. Kernel will assign 127.0.0.1 for us. */
61 strcpy(ifr
.ifr_name
, "lo");
62 if (ioctl(sock
, SIOCGIFFLAGS
, &ifr
) < 0)
63 err(1, "ioctl(SIOCGIFFLAGS) failed");
65 /* The kernel preserves ifr.ifr_name for use. */
66 ifr
.ifr_flags
|= IFF_UP
| IFF_RUNNING
;
67 if (ioctl(sock
, SIOCSIFFLAGS
, &ifr
) < 0)
68 err(1, "ioctl(SIOCSIFFLAGS) failed");
73 static char **title_argv
;
74 static void setproctitle(const char *title
)
76 /* Hopefully 1k is all we ever need. */
78 memset(newtitle
, 0, sizeof(newtitle
));
79 int len
= sprintf(newtitle
, "%s: %s [pid ns]", PROG
, title
);
81 prctl(PR_SET_NAME
, (uintptr_t)newtitle
);
83 /* Clobber argv to set the title. Need to figure out how much space though. */
86 while (title_argv
[argc
])
87 i
+= strlen(title_argv
[argc
++]) + 1;
88 /* Now scan the environ table. */
89 while (title_argv
[argc
])
90 i
+= strlen(title_argv
[argc
++]) + 1;
94 /* This will NUL pad the string for us too. */
95 strncpy(title_argv
[0], newtitle
, i
);
98 static void close_fds(void)
101 for (i
= 3; i
< 10; ++i
)
105 static void exit_as_status_ext(int status
)
108 int exit_status
= WEXITSTATUS(status
);
110 if (WIFSIGNALED(status
)) {
111 sig_status
= WTERMSIG(status
);
112 } else if (exit_status
> 128) {
113 /* For the external init, translate the signal status back.
114 * TODO: This gets it wrong when the child actually exited.
115 * We need to set up a pipe between the two inits so we can
116 * get back the proper details.
118 sig_status
= exit_status
- 128;
122 signal(sig_status
, SIG_DFL
);
123 kill(getpid(), sig_status
);
125 /* Still here ? Maybe the signal was masked. Just exit. */
126 exit_status
= 128 + sig_status
;
132 static void exit_as_status_int(int status
)
134 /* If we are the init for the pid ns, we can't kill ourselves --
135 * the kernel explicitly disallows this. Just exit with a high
136 * status value instead. Our parent will handle it themselves.
140 if (WIFSIGNALED(status
))
141 exit_status
= 128 + WTERMSIG(status
);
143 exit_status
= WEXITSTATUS(status
);
148 static int reap_children(void)
160 static pid_t child_pid
;
161 static void signal_passthru(int sig
, siginfo_t
*siginfo
, void *context
)
166 /* If the signal is coming from our children, ignore it.
167 * If it's coming from outside the pid ns, pass it along.
169 if (siginfo
->si_pid
!= 0)
172 /* Kill all the children! */
177 /* Just forward signal to the child. */
178 kill(child_pid
, sig
);
182 /* We want to forward some signals to the child process. Block the rest.
183 * We don't actually exit as we wait for the child to die/process the signal
184 * first, and then we'll kill/exit after that point.
186 static void setup_signal_handler(pid_t pid
)
190 struct sigaction sa
= {
191 .sa_sigaction
= signal_passthru
,
192 .sa_flags
= SA_SIGINFO
| SA_RESTART
,
197 for (i
= 1; i
< SIGUNUSED
; ++i
)
198 if (sigaction(i
, &sa
, NULL
) && errno
!= EINVAL
)
199 fprintf(stderr
, "sigaction(%i) failed: %s\n", i
, strerror(errno
));
200 for (i
= SIGRTMIN
; i
<= SIGRTMAX
; ++i
)
201 if (sigaction(i
, &sa
, NULL
) && errno
!= EINVAL
)
202 fprintf(stderr
, "sigaction(%i) failed: %s\n", i
, strerror(errno
));
204 /* As an init, we will reap the children via wait(). */
205 signal(SIGCHLD
, SIG_DFL
);
208 static bool unshare_pid(bool daemonize
)
210 if (!vunshare(CLONE_NEWPID
))
215 /* Set up external init process. */
218 case -1: err(1, "fork() failed");
223 setproctitle("ext init");
224 setup_signal_handler(pid
);
226 exit_as_status_ext(reap_children());
231 err(1, "setsid() failed");
233 int fd
= open("/dev/null", O_RDWR
);
235 err(1, "open(/dev/null) failed");
236 if (dup2(fd
, 0) == -1 || dup2(fd
, 1) == -1 || dup2(fd
, 2) == -1)
237 err(1, "dup2() failed");
242 /* Set up fresh /proc. */
243 if (mount("none", "/proc", 0, MS_PRIVATE
| MS_REC
, ""))
244 err(1, "mount(/proc, MS_PRIVATE) failed");
245 if (mount("proc", "/proc", "proc", MS_NOSUID
| MS_NODEV
| MS_NOEXEC
| MS_RELATIME
, ""))
246 err(1, "mount(/proc) failed");
248 /* Set up internal init process. */
251 case -1: err(1, "fork() failed");
254 setproctitle("int init");
255 setup_signal_handler(pid
);
257 exit_as_status_int(reap_children());
263 static void map_uid_gid(uid_t iuid
, gid_t igid
, uid_t ouid
, gid_t ogid
)
267 fp
= fopen("/proc/self/setgroups", "w");
273 fp
= fopen("/proc/self/uid_map", "w");
274 fprintf(fp
, "%u %u 1\n", iuid
, ouid
);
277 fp
= fopen("/proc/self/gid_map", "w");
278 fprintf(fp
, "%u %u 1\n", igid
, ogid
);
282 #define a_argument required_argument
283 static const struct option opts
[] = {
284 { "pid", a_argument
, NULL
, 1 },
285 { NULL
, 0, NULL
, 0 },
288 static void usage(void)
291 "Usage: unshare [options] <program>\n"
293 "Options: [DimnpuU]\n"
294 " -i Use IPC namespaces\n"
295 " -m Use mount namespaces\n"
296 " -n Use net namespaces\n"
297 " -p Use pid namespaces\n"
298 " -u Use UTS namespaces\n"
299 " -U Use user namespaces\n"
300 " -D Daemonize program"
305 int main(int argc
, char *argv
[])
309 const char *pid
= NULL
;
316 bool daemonize
= false;
322 while ((c
= getopt_long(argc
, argv
, "+DimnpuU", opts
, NULL
)) != -1) {
327 case 'i': newipc
= true; break;
328 case 'm': newmnt
= true; break;
329 case 'n': newnet
= true; break;
330 case 'p': newpid
= true; break;
331 case 'u': newuts
= true; break;
332 case 'U': newusr
= true; break;
333 case 'D': daemonize
= true; break;
347 if (vunshare(CLONE_NEWUSER
))
348 map_uid_gid(0, 0, uid
, gid
);
353 if (newmnt
|| newpid
)
354 vunshare(CLONE_NEWNS
);
356 vunshare(CLONE_NEWUTS
);
358 vunshare(CLONE_NEWIPC
);
363 pidfp
= fopen(pid
, "we");
365 err(1, "fopen(%s) failed", pid
);
368 if (newpid
&& unshare_pid(daemonize
)) {
370 } else if (daemonize
)
372 err(1, "daemon() failed");
375 fprintf(pidfp
, "%u\n", getpid());
380 if (vunshare(CLONE_NEWUSER
))
381 map_uid_gid(uid
, gid
, 0, 0);
383 execvp(argv
[0], argv
);
384 fprintf(stderr
, "%s: %s\n", argv
[0], strerror(errno
));