Gentoo Archives: gentoo-commits

From: Mike Frysinger <vapier@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/sandbox:master commit in: /, src/, etc/
Date: Sun, 27 Sep 2015 06:27:28
Message-Id: 1443334889.d96587d02f203e2c40790ac1c0e5778c6299d1a2.vapier@gentoo
1 commit: d96587d02f203e2c40790ac1c0e5778c6299d1a2
2 Author: Mike Frysinger <vapier <AT> gentoo <DOT> org>
3 AuthorDate: Mon Sep 21 01:08:02 2015 +0000
4 Commit: Mike Frysinger <vapier <AT> gentoo <DOT> org>
5 CommitDate: Sun Sep 27 06:21:29 2015 +0000
6 URL: https://gitweb.gentoo.org/proj/sandbox.git/commit/?id=d96587d0
7
8 sandbox: enable support for linux namespaces
9
10 This initial version doesn't enable their use by default.
11
12 URL: https://bugs.gentoo.org/512794
13 Reported-by: Matthew Thode <prometheanfire <AT> gentoo.org>
14 Signed-off-by: Mike Frysinger <vapier <AT> gentoo.org>
15
16 configure.ac | 5 ++
17 etc/sandbox.conf | 20 +++++
18 headers.h | 12 +++
19 src/Makefile.am | 1 +
20 src/namespaces.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
21 src/options.c | 71 ++++++++++++++++++
22 src/sandbox.c | 2 +-
23 src/sandbox.h | 14 ++++
24 8 files changed, 343 insertions(+), 1 deletion(-)
25
26 diff --git a/configure.ac b/configure.ac
27 index 4c4cb20..843bb97 100644
28 --- a/configure.ac
29 +++ b/configure.ac
30 @@ -117,6 +117,7 @@ AC_CHECK_HEADERS_ONCE(m4_flatten([
31 memory.h
32 pthread.h
33 pwd.h
34 + sched.h
35 siginfo.h
36 signal.h
37 sigsegv.h
38 @@ -132,10 +133,13 @@ AC_CHECK_HEADERS_ONCE(m4_flatten([
39 unistd.h
40 utime.h
41 sys/file.h
42 + sys/ioctl.h
43 sys/mman.h
44 + sys/mount.h
45 sys/param.h
46 sys/ptrace.h
47 sys/reg.h
48 + sys/socket.h
49 sys/stat.h
50 sys/syscall.h
51 sys/time.h
52 @@ -225,6 +229,7 @@ AC_CHECK_FUNCS_ONCE(m4_flatten([
53 symlinkat
54 truncate64
55 unlinkat
56 + unshare
57 utime
58 utimensat
59 utimes
60
61 diff --git a/etc/sandbox.conf b/etc/sandbox.conf
62 index 1d7655c..5f09ee4 100644
63 --- a/etc/sandbox.conf
64 +++ b/etc/sandbox.conf
65 @@ -29,6 +29,26 @@
66
67
68 #
69 +# Namespace Section (Linux-only)
70 +#
71 +
72 +# Global knob to control all namespaces.
73 +#NAMESPACES_ENABLE="no"
74 +
75 +# Knobs for different types of namespaces. If the runtime doesn't support a
76 +# particular type, it will be automatically skipped. Default to off as these
77 +# are currently experimental.
78 +# For more details on each type, see the namespaces(7) manpage.
79 +#NAMESPACE_IPC_ENABLE="no"
80 +#NAMESPACE_MNT_ENABLE="no"
81 +#NAMESPACE_NET_ENABLE="no"
82 +#NAMESPACE_PID_ENABLE="no"
83 +#NAMESPACE_SYSV_ENABLE="no"
84 +#NAMESPACE_USER_ENABLE="no"
85 +#NAMESPACE_UTS_ENABLE="no"
86 +
87 +
88 +#
89 # ACCESS Section
90 #
91
92
93 diff --git a/headers.h b/headers.h
94 index 458958b..13e005a 100644
95 --- a/headers.h
96 +++ b/headers.h
97 @@ -53,6 +53,9 @@
98 #ifdef HAVE_PWD_H
99 # include <pwd.h>
100 #endif
101 +#ifdef HAVE_SCHED_H
102 +# include <sched.h>
103 +#endif
104 #ifdef HAVE_SIGINFO_H
105 # include <siginfo.h>
106 #endif
107 @@ -96,11 +99,17 @@
108 #ifdef HAVE_SYS_FILE_H
109 # include <sys/file.h>
110 #endif
111 +#ifdef HAVE_SYS_IOCTL_H
112 +# include <sys/ioctl.h>
113 +#endif
114 #ifdef HAVE_SYS_MMAN_H
115 # include <sys/mman.h>
116 #else
117 #error
118 #endif
119 +#ifdef HAVE_SYS_MOUNT_H
120 +# include <sys/mount.h>
121 +#endif
122 #ifdef HAVE_SYS_PARAM_H
123 # include <sys/param.h>
124 #endif
125 @@ -110,6 +119,9 @@
126 #ifdef HAVE_SYS_REG_H
127 # include <sys/reg.h>
128 #endif
129 +#ifdef HAVE_SYS_SOCKET_H
130 +# include <sys/socket.h>
131 +#endif
132 #ifdef HAVE_SYS_STAT_H
133 # include <sys/stat.h>
134 #endif
135
136 diff --git a/src/Makefile.am b/src/Makefile.am
137 index 24ffdcf..e7aeb30 100644
138 --- a/src/Makefile.am
139 +++ b/src/Makefile.am
140 @@ -11,6 +11,7 @@ AM_CPPFLAGS = \
141 sandbox_LDADD = $(top_builddir)/libsbutil/libsbutil.la $(LIBDL)
142 sandbox_SOURCES = \
143 environ.c \
144 + namespaces.c \
145 options.c \
146 sandbox.h \
147 sandbox.c
148
149 diff --git a/src/namespaces.c b/src/namespaces.c
150 new file mode 100644
151 index 0000000..5be42f6
152 --- /dev/null
153 +++ b/src/namespaces.c
154 @@ -0,0 +1,219 @@
155 +/*
156 + * Initialize various namespaces
157 + *
158 + * Copyright 1999-2015 Gentoo Foundation
159 + * Licensed under the GPL-2
160 + */
161 +
162 +#include "headers.h"
163 +#include "sbutil.h"
164 +#include "sandbox.h"
165 +
166 +#ifdef __linux__
167 +
168 +#include <net/if.h>
169 +
170 +#ifndef HAVE_UNSHARE
171 +# ifdef __NR_unshare
172 +# define unshare(x) syscall(__NR_unshare, x)
173 +# else
174 +# define unshare(x) -1
175 +# endif
176 +#endif
177 +
178 +#define xmount(...) sb_assert(mount(__VA_ARGS__) == 0)
179 +#define xmkdir(...) sb_assert(mkdir(__VA_ARGS__) == 0)
180 +#define xchmod(...) sb_assert(chmod(__VA_ARGS__) == 0)
181 +#define xsymlink(...) sb_assert(symlink(__VA_ARGS__) == 0)
182 +
183 +#define xasprintf(fmt, ...) \
184 +({ \
185 + int _ret = asprintf(fmt, __VA_ARGS__); \
186 + if (_ret == 0) \
187 + sb_perr("asprintf(%s) failed", #fmt); \
188 + _ret; \
189 +})
190 +#define xfopen(path, ...) \
191 +({ \
192 + FILE *_ret = fopen(path, __VA_ARGS__); \
193 + if (_ret == 0) \
194 + sb_perr("fopen(%s) failed", #path); \
195 + _ret; \
196 +})
197 +
198 +static void ns_user_switch(int uid, int gid, int nuid, int ngid)
199 +{
200 +#ifdef CLONE_NEWUSER
201 + FILE *fp;
202 + char *map;
203 +
204 + if (uid == nuid || unshare(CLONE_NEWUSER))
205 + return;
206 +
207 + fp = xfopen("/proc/self/uid_map", "we");
208 + xasprintf(&map, "%i %i 1", nuid, uid);
209 + fputs(map, fp);
210 + fclose(fp);
211 + free(map);
212 +
213 + fp = xfopen("/proc/self/setgroups", "we");
214 + fputs("deny", fp);
215 + fclose(fp);
216 +
217 + fp = xfopen("/proc/self/gid_map", "we");
218 + xasprintf(&map, "%i %i 1\n", ngid, gid);
219 + fputs(map, fp);
220 + fclose(fp);
221 + free(map);
222 +#endif
223 +}
224 +
225 +static void ns_net_setup(void)
226 +{
227 +#ifdef CLONE_NEWNET
228 + if (unshare(CLONE_NEWNET))
229 + return;
230 +
231 + int sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0);
232 + struct ifreq ifr;
233 +
234 + strcpy(ifr.ifr_name, "lo");
235 + if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
236 + sb_perr("ioctl(SIOCGIFFLAGS, lo) failed");
237 + strcpy(ifr.ifr_name, "lo");
238 + ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
239 + if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
240 + sb_perr("ioctl(SIOCSIFFLAGS, lo) failed");
241 +#endif
242 +}
243 +
244 +/* Create a nice empty /dev for playing in. */
245 +static void ns_mount_setup(void)
246 +{
247 +#ifdef CLONE_NEWNS
248 + /* Create a new mount namespace. */
249 + if (unshare(CLONE_NEWNS))
250 + return;
251 +
252 + /* Mark the whole tree as private so we don't mess up the parent ns. */
253 + if (mount("none", "/", NULL, MS_PRIVATE | MS_REC, NULL))
254 + return;
255 +
256 + /* Create a unique /tmp dir for everyone. */
257 + if (mount("/tmp", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_RELATIME, NULL))
258 + sb_ewarn("could not mount /tmp");
259 +
260 + /* Mount an empty dir inside of /dev which we'll populate with bind mounts
261 + * to the existing files in /dev. We can't just mknod ourselves because
262 + * the kernel will deny those calls when we aren't actually root. We pick
263 + * the /dev/shm dir as it should generally exist and we don't care about
264 + * binding its contents. */
265 + if (mount("sandbox-dev", "/dev/shm", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_RELATIME, "mode=0755"))
266 + return;
267 +
268 + /* Now map in all the files/dirs we do want to expose. */
269 + int fd;
270 +#define bind_file(node) \
271 + fd = open("/dev/shm/" node, O_CREAT, 0); \
272 + sb_assert(fd != -1); \
273 + close(fd); \
274 + xmount("/dev/" node, "/dev/shm/" node, NULL, MS_BIND, NULL)
275 +#define bind_dir(node) \
276 + xmkdir("/dev/shm/" node, 0); \
277 + xmount("/dev/" node, "/dev/shm/" node, NULL, MS_BIND, NULL)
278 +
279 + bind_file("full");
280 + bind_file("null");
281 + bind_file("ptmx");
282 + bind_file("tty");
283 + bind_file("urandom");
284 + bind_file("zero");
285 + bind_dir("pts");
286 +
287 + xmkdir("/dev/shm/shm", 01777);
288 + xchmod("/dev/shm/shm", 01777);
289 +
290 + xsymlink("/proc/self/fd", "/dev/shm/fd");
291 + xsymlink("fd/0", "/dev/shm/stdin");
292 + xsymlink("fd/1", "/dev/shm/stdout");
293 + xsymlink("fd/2", "/dev/shm/stderr");
294 +
295 + xchmod("/dev/shm", 0555);
296 +
297 + /* Now that the new root looks good, move it to /dev. */
298 + xmount("/dev/shm", "/dev", NULL, MS_MOVE, NULL);
299 +#endif
300 +}
301 +
302 +static pid_t ns_pid_setup(void)
303 +{
304 + pid_t pid;
305 +
306 + if (unshare(CLONE_NEWPID) == 0) {
307 + /* Create a child in the new pid ns. */
308 + pid = fork();
309 + if (pid == 0) {
310 + /* Create a new mount namespace for the child. */
311 + sb_assert(unshare(CLONE_NEWNS) == 0);
312 + xmount("none", "/proc", NULL, MS_PRIVATE | MS_REC, NULL);
313 + xmount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, NULL);
314 + }
315 + } else {
316 + /* At least hide other procs. */
317 + if (umount2("/proc", MNT_FORCE | MNT_DETACH) == 0)
318 + xmount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "hidepid=2");
319 + pid = fork();
320 + }
321 +
322 + return pid;
323 +}
324 +
325 +pid_t setup_namespaces(void)
326 +{
327 + /* We need to unshare namespaces independently anyways as users can
328 + * configure kernels to have only some enabled, and if we try to do
329 + * them all at once, we'll get EINVAL. */
330 +
331 + int uid = getuid();
332 + int gid = getgid();
333 + pid_t pid;
334 +
335 + /* This comes first so we can do the others as non-root. */
336 + if (opt_use_ns_user)
337 + ns_user_switch(uid, gid, 0, 0);
338 +
339 +#ifdef CLONE_NEWIPC
340 + if (opt_use_ns_ipc)
341 + unshare(CLONE_NEWIPC);
342 +#endif
343 +#ifdef CLONE_SYSVSEM
344 + if (opt_use_ns_sysv)
345 + unshare(CLONE_SYSVSEM);
346 +#endif
347 +
348 +#ifdef CLONE_NEWUTS
349 + if (opt_use_ns_uts && unshare(CLONE_NEWUTS) == 0) {
350 + const char name[] = "gentoo-sandbox";
351 + if (sethostname(name, sizeof(name) - 1))
352 + /* silence gcc warning */;
353 + }
354 +#endif
355 +
356 + if (opt_use_ns_net)
357 + ns_net_setup();
358 +
359 + if (opt_use_ns_mnt)
360 + ns_mount_setup();
361 +
362 + if (opt_use_ns_mnt && opt_use_ns_pid)
363 + pid = ns_pid_setup();
364 + else
365 + pid = fork();
366 +
367 + if (opt_use_ns_user)
368 + ns_user_switch(0, 0, uid, gid);
369 +
370 + return pid;
371 +}
372 +
373 +#endif
374
375 diff --git a/src/options.c b/src/options.c
376 index 10f937c..295ee75 100644
377 --- a/src/options.c
378 +++ b/src/options.c
379 @@ -9,6 +9,43 @@
380 #include "sbutil.h"
381 #include "sandbox.h"
382
383 +/* Setting to -1 will load defaults from the config file. */
384 +int opt_use_namespaces = -1;
385 +int opt_use_ns_ipc = -1;
386 +int opt_use_ns_mnt = -1;
387 +int opt_use_ns_net = -1;
388 +int opt_use_ns_pid = -1;
389 +int opt_use_ns_sysv = -1;
390 +int opt_use_ns_user = -1;
391 +int opt_use_ns_uts = -1;
392 +
393 +static const struct {
394 + const char *name;
395 + int *opt;
396 + int default_val;
397 +} config_opts[] = {
398 + /* Default these to off until they can get more testing. */
399 + { "NAMESPACES_ENABLE", &opt_use_namespaces, false, },
400 + { "NAMESPACE_IPC_ENABLE", &opt_use_ns_ipc, false, },
401 + { "NAMESPACE_MNT_ENABLE", &opt_use_ns_mnt, false, },
402 + { "NAMESPACE_NET_ENABLE", &opt_use_ns_net, false, },
403 + { "NAMESPACE_PID_ENABLE", &opt_use_ns_pid, false, },
404 + { "NAMESPACE_SYSV_ENABLE", &opt_use_ns_sysv, false, },
405 + { "NAMESPACE_USER_ENABLE", &opt_use_ns_user, false, },
406 + { "NAMESPACE_UTS_ENABLE", &opt_use_ns_uts, false, },
407 +};
408 +
409 +static void read_config(void)
410 +{
411 + size_t i;
412 +
413 + for (i = 0; i < ARRAY_SIZE(config_opts); ++i) {
414 + int *opt = config_opts[i].opt;
415 + if (*opt == -1)
416 + *opt = sb_get_cnf_bool(config_opts[i].name, config_opts[i].default_val);
417 + }
418 +}
419 +
420 static void show_version(void)
421 {
422 puts(
423 @@ -36,11 +73,43 @@ static void show_version(void)
424 #define PARSE_FLAGS "+hV"
425 #define a_argument required_argument
426 static struct option const long_opts[] = {
427 + {"ns-on", no_argument, &opt_use_namespaces, true},
428 + {"ns-off", no_argument, &opt_use_namespaces, false},
429 + {"ns-ipc-on", no_argument, &opt_use_ns_ipc, true},
430 + {"ns-ipc-off", no_argument, &opt_use_ns_ipc, false},
431 + {"ns-mnt-on", no_argument, &opt_use_ns_mnt, true},
432 + {"ns-mnt-off", no_argument, &opt_use_ns_mnt, false},
433 + {"ns-net-on", no_argument, &opt_use_ns_net, true},
434 + {"ns-net-off", no_argument, &opt_use_ns_net, false},
435 + {"ns-pid-on", no_argument, &opt_use_ns_pid, true},
436 + {"ns-pid-off", no_argument, &opt_use_ns_pid, false},
437 + {"ns-sysv-on", no_argument, &opt_use_ns_sysv, true},
438 + {"ns-sysv-off", no_argument, &opt_use_ns_sysv, false},
439 + {"ns-user-on", no_argument, &opt_use_ns_user, true},
440 + {"ns-user-off", no_argument, &opt_use_ns_user, false},
441 + {"ns-uts-on", no_argument, &opt_use_ns_uts, true},
442 + {"ns-uts-off", no_argument, &opt_use_ns_uts, false},
443 {"help", no_argument, NULL, 'h'},
444 {"version", no_argument, NULL, 'V'},
445 {NULL, no_argument, NULL, 0x0}
446 };
447 static const char * const opts_help[] = {
448 + "Enable the use of namespaces",
449 + "Disable the use of namespaces",
450 + "Enable the use of IPC (and System V) namespaces",
451 + "Disable the use of IPC (and System V) namespaces",
452 + "Enable the use of mount namespaces",
453 + "Disable the use of mount namespaces",
454 + "Enable the use of network namespaces",
455 + "Disable the use of network namespaces",
456 + "Enable the use of process (pid) namespaces",
457 + "Disable the use of process (pid) namespaces",
458 + "Enable the use of System V namespaces",
459 + "Disable the use of System V namespaces",
460 + "Enable the use of user namespaces",
461 + "Disable the use of user namespaces",
462 + "Enable the use of UTS (hostname/uname) namespaces",
463 + "Disable the use of UTS (hostname/uname) namespaces",
464 "Print this help and exit",
465 "Print version and exit",
466 NULL
467 @@ -113,4 +182,6 @@ void parseargs(int argc, char *argv[])
468 show_usage(1);
469 }
470 }
471 +
472 + read_config();
473 }
474
475 diff --git a/src/sandbox.c b/src/sandbox.c
476 index 15c87b2..c668ab6 100644
477 --- a/src/sandbox.c
478 +++ b/src/sandbox.c
479 @@ -160,7 +160,7 @@ static int spawn_shell(char *argv_bash[], char **env, int debug)
480 int status = 0;
481 int ret = 0;
482
483 - child_pid = fork();
484 + child_pid = opt_use_namespaces ? setup_namespaces() : fork();
485
486 /* Child's process */
487 if (0 == child_pid) {
488
489 diff --git a/src/sandbox.h b/src/sandbox.h
490 index 4233bd6..303dac4 100644
491 --- a/src/sandbox.h
492 +++ b/src/sandbox.h
493 @@ -28,6 +28,12 @@ extern char **setup_environ(struct sandbox_info_t *sandbox_info);
494
495 extern bool sb_get_cnf_bool(const char *, bool);
496
497 +#ifdef __linux__
498 +extern pid_t setup_namespaces(void);
499 +#else
500 +#define setup_namespaces() fork()
501 +#endif
502 +
503 #define sb_warn(fmt, args...) fprintf(stderr, "%s:%s " fmt "\n", "sandbox", __func__, ## args)
504 #define sb_pwarn(fmt, args...) sb_warn(fmt ": %s\n", ## args, strerror(errno))
505 #define _sb_err(func, fmt, args...) do { sb_##func(fmt, ## args); exit(EXIT_FAILURE); } while (0)
506 @@ -36,5 +42,13 @@ extern bool sb_get_cnf_bool(const char *, bool);
507
508 /* Option parsing related code */
509 extern void parseargs(int argc, char *argv[]);
510 +extern int opt_use_namespaces;
511 +extern int opt_use_ns_ipc;
512 +extern int opt_use_ns_mnt;
513 +extern int opt_use_ns_net;
514 +extern int opt_use_ns_pid;
515 +extern int opt_use_ns_sysv;
516 +extern int opt_use_ns_user;
517 +extern int opt_use_ns_uts;
518
519 #endif