public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/sandbox:master commit in: /, src/, etc/
@ 2015-09-27  6:27 Mike Frysinger
  0 siblings, 0 replies; only message in thread
From: Mike Frysinger @ 2015-09-27  6:27 UTC (permalink / raw
  To: gentoo-commits

commit:     d96587d02f203e2c40790ac1c0e5778c6299d1a2
Author:     Mike Frysinger <vapier <AT> gentoo <DOT> org>
AuthorDate: Mon Sep 21 01:08:02 2015 +0000
Commit:     Mike Frysinger <vapier <AT> gentoo <DOT> org>
CommitDate: Sun Sep 27 06:21:29 2015 +0000
URL:        https://gitweb.gentoo.org/proj/sandbox.git/commit/?id=d96587d0

sandbox: enable support for linux namespaces

This initial version doesn't enable their use by default.

URL: https://bugs.gentoo.org/512794
Reported-by: Matthew Thode <prometheanfire <AT> gentoo.org>
Signed-off-by: Mike Frysinger <vapier <AT> gentoo.org>

 configure.ac     |   5 ++
 etc/sandbox.conf |  20 +++++
 headers.h        |  12 +++
 src/Makefile.am  |   1 +
 src/namespaces.c | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/options.c    |  71 ++++++++++++++++++
 src/sandbox.c    |   2 +-
 src/sandbox.h    |  14 ++++
 8 files changed, 343 insertions(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index 4c4cb20..843bb97 100644
--- a/configure.ac
+++ b/configure.ac
@@ -117,6 +117,7 @@ AC_CHECK_HEADERS_ONCE(m4_flatten([
 	memory.h
 	pthread.h
 	pwd.h
+	sched.h
 	siginfo.h
 	signal.h
 	sigsegv.h
@@ -132,10 +133,13 @@ AC_CHECK_HEADERS_ONCE(m4_flatten([
 	unistd.h
 	utime.h
 	sys/file.h
+	sys/ioctl.h
 	sys/mman.h
+	sys/mount.h
 	sys/param.h
 	sys/ptrace.h
 	sys/reg.h
+	sys/socket.h
 	sys/stat.h
 	sys/syscall.h
 	sys/time.h
@@ -225,6 +229,7 @@ AC_CHECK_FUNCS_ONCE(m4_flatten([
 	symlinkat
 	truncate64
 	unlinkat
+	unshare
 	utime
 	utimensat
 	utimes

diff --git a/etc/sandbox.conf b/etc/sandbox.conf
index 1d7655c..5f09ee4 100644
--- a/etc/sandbox.conf
+++ b/etc/sandbox.conf
@@ -29,6 +29,26 @@
 
 
 #
+# Namespace Section (Linux-only)
+#
+
+# Global knob to control all namespaces.
+#NAMESPACES_ENABLE="no"
+
+# Knobs for different types of namespaces.  If the runtime doesn't support a
+# particular type, it will be automatically skipped.  Default to off as these
+# are currently experimental.
+# For more details on each type, see the namespaces(7) manpage.
+#NAMESPACE_IPC_ENABLE="no"
+#NAMESPACE_MNT_ENABLE="no"
+#NAMESPACE_NET_ENABLE="no"
+#NAMESPACE_PID_ENABLE="no"
+#NAMESPACE_SYSV_ENABLE="no"
+#NAMESPACE_USER_ENABLE="no"
+#NAMESPACE_UTS_ENABLE="no"
+
+
+#
 # ACCESS Section
 #
 

diff --git a/headers.h b/headers.h
index 458958b..13e005a 100644
--- a/headers.h
+++ b/headers.h
@@ -53,6 +53,9 @@
 #ifdef HAVE_PWD_H
 # include <pwd.h>
 #endif
+#ifdef HAVE_SCHED_H
+# include <sched.h>
+#endif
 #ifdef HAVE_SIGINFO_H
 # include <siginfo.h>
 #endif
@@ -96,11 +99,17 @@
 #ifdef HAVE_SYS_FILE_H
 # include <sys/file.h>
 #endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
 #ifdef HAVE_SYS_MMAN_H
 # include <sys/mman.h>
 #else
 #error
 #endif
+#ifdef HAVE_SYS_MOUNT_H
+# include <sys/mount.h>
+#endif
 #ifdef HAVE_SYS_PARAM_H
 # include <sys/param.h>
 #endif
@@ -110,6 +119,9 @@
 #ifdef HAVE_SYS_REG_H
 # include <sys/reg.h>
 #endif
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
 #ifdef HAVE_SYS_STAT_H
 # include <sys/stat.h>
 #endif

diff --git a/src/Makefile.am b/src/Makefile.am
index 24ffdcf..e7aeb30 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,6 +11,7 @@ AM_CPPFLAGS = \
 sandbox_LDADD = $(top_builddir)/libsbutil/libsbutil.la $(LIBDL)
 sandbox_SOURCES = \
 	environ.c \
+	namespaces.c \
 	options.c \
 	sandbox.h \
 	sandbox.c

diff --git a/src/namespaces.c b/src/namespaces.c
new file mode 100644
index 0000000..5be42f6
--- /dev/null
+++ b/src/namespaces.c
@@ -0,0 +1,219 @@
+/*
+ * Initialize various namespaces
+ *
+ * Copyright 1999-2015 Gentoo Foundation
+ * Licensed under the GPL-2
+ */
+
+#include "headers.h"
+#include "sbutil.h"
+#include "sandbox.h"
+
+#ifdef __linux__
+
+#include <net/if.h>
+
+#ifndef HAVE_UNSHARE
+# ifdef __NR_unshare
+#  define unshare(x) syscall(__NR_unshare, x)
+# else
+#  define unshare(x) -1
+# endif
+#endif
+
+#define xmount(...) sb_assert(mount(__VA_ARGS__) == 0)
+#define xmkdir(...) sb_assert(mkdir(__VA_ARGS__) == 0)
+#define xchmod(...) sb_assert(chmod(__VA_ARGS__) == 0)
+#define xsymlink(...) sb_assert(symlink(__VA_ARGS__) == 0)
+
+#define xasprintf(fmt, ...) \
+({ \
+	int _ret = asprintf(fmt, __VA_ARGS__); \
+	if (_ret == 0) \
+		sb_perr("asprintf(%s) failed", #fmt); \
+	_ret; \
+})
+#define xfopen(path, ...) \
+({ \
+	FILE *_ret = fopen(path, __VA_ARGS__); \
+	if (_ret == 0) \
+		sb_perr("fopen(%s) failed", #path); \
+	_ret; \
+})
+
+static void ns_user_switch(int uid, int gid, int nuid, int ngid)
+{
+#ifdef CLONE_NEWUSER
+	FILE *fp;
+	char *map;
+
+	if (uid == nuid || unshare(CLONE_NEWUSER))
+		return;
+
+	fp = xfopen("/proc/self/uid_map", "we");
+	xasprintf(&map, "%i %i 1", nuid, uid);
+	fputs(map, fp);
+	fclose(fp);
+	free(map);
+
+	fp = xfopen("/proc/self/setgroups", "we");
+	fputs("deny", fp);
+	fclose(fp);
+
+	fp = xfopen("/proc/self/gid_map", "we");
+	xasprintf(&map, "%i %i 1\n", ngid, gid);
+	fputs(map, fp);
+	fclose(fp);
+	free(map);
+#endif
+}
+
+static void ns_net_setup(void)
+{
+#ifdef CLONE_NEWNET
+	if (unshare(CLONE_NEWNET))
+		return;
+
+	int sock = socket(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+	struct ifreq ifr;
+
+	strcpy(ifr.ifr_name, "lo");
+	if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0)
+		sb_perr("ioctl(SIOCGIFFLAGS, lo) failed");
+	strcpy(ifr.ifr_name, "lo");
+	ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
+	if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0)
+		sb_perr("ioctl(SIOCSIFFLAGS, lo) failed");
+#endif
+}
+
+/* Create a nice empty /dev for playing in. */
+static void ns_mount_setup(void)
+{
+#ifdef CLONE_NEWNS
+	/* Create a new mount namespace. */
+	if (unshare(CLONE_NEWNS))
+		return;
+
+	/* Mark the whole tree as private so we don't mess up the parent ns. */
+	if (mount("none", "/", NULL, MS_PRIVATE | MS_REC, NULL))
+		return;
+
+	/* Create a unique /tmp dir for everyone. */
+	if (mount("/tmp", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_RELATIME, NULL))
+		sb_ewarn("could not mount /tmp");
+
+	/* Mount an empty dir inside of /dev which we'll populate with bind mounts
+	 * to the existing files in /dev.  We can't just mknod ourselves because
+	 * the kernel will deny those calls when we aren't actually root.  We pick
+	 * the /dev/shm dir as it should generally exist and we don't care about
+	 * binding its contents. */
+	if (mount("sandbox-dev", "/dev/shm", "tmpfs", MS_NOSUID | MS_NOEXEC | MS_RELATIME, "mode=0755"))
+		return;
+
+	/* Now map in all the files/dirs we do want to expose. */
+	int fd;
+#define bind_file(node) \
+	fd = open("/dev/shm/" node, O_CREAT, 0); \
+	sb_assert(fd != -1); \
+	close(fd); \
+	xmount("/dev/" node, "/dev/shm/" node, NULL, MS_BIND, NULL)
+#define bind_dir(node) \
+	xmkdir("/dev/shm/" node, 0); \
+	xmount("/dev/" node, "/dev/shm/" node, NULL, MS_BIND, NULL)
+
+	bind_file("full");
+	bind_file("null");
+	bind_file("ptmx");
+	bind_file("tty");
+	bind_file("urandom");
+	bind_file("zero");
+	bind_dir("pts");
+
+	xmkdir("/dev/shm/shm", 01777);
+	xchmod("/dev/shm/shm", 01777);
+
+	xsymlink("/proc/self/fd", "/dev/shm/fd");
+	xsymlink("fd/0", "/dev/shm/stdin");
+	xsymlink("fd/1", "/dev/shm/stdout");
+	xsymlink("fd/2", "/dev/shm/stderr");
+
+	xchmod("/dev/shm", 0555);
+
+	/* Now that the new root looks good, move it to /dev. */
+	xmount("/dev/shm", "/dev", NULL, MS_MOVE, NULL);
+#endif
+}
+
+static pid_t ns_pid_setup(void)
+{
+	pid_t pid;
+
+	if (unshare(CLONE_NEWPID) == 0) {
+		/* Create a child in the new pid ns. */
+		pid = fork();
+		if (pid == 0) {
+			/* Create a new mount namespace for the child. */
+			sb_assert(unshare(CLONE_NEWNS) == 0);
+			xmount("none", "/proc", NULL, MS_PRIVATE | MS_REC, NULL);
+			xmount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, NULL);
+		}
+	} else {
+		/* At least hide other procs. */
+		if (umount2("/proc", MNT_FORCE | MNT_DETACH) == 0)
+			xmount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_RELATIME, "hidepid=2");
+		pid = fork();
+	}
+
+	return pid;
+}
+
+pid_t setup_namespaces(void)
+{
+	/* We need to unshare namespaces independently anyways as users can
+	 * configure kernels to have only some enabled, and if we try to do
+	 * them all at once, we'll get EINVAL. */
+
+	int uid = getuid();
+	int gid = getgid();
+	pid_t pid;
+
+	/* This comes first so we can do the others as non-root. */
+	if (opt_use_ns_user)
+		ns_user_switch(uid, gid, 0, 0);
+
+#ifdef CLONE_NEWIPC
+	if (opt_use_ns_ipc)
+		unshare(CLONE_NEWIPC);
+#endif
+#ifdef CLONE_SYSVSEM
+	if (opt_use_ns_sysv)
+		unshare(CLONE_SYSVSEM);
+#endif
+
+#ifdef CLONE_NEWUTS
+	if (opt_use_ns_uts && unshare(CLONE_NEWUTS) == 0) {
+		const char name[] = "gentoo-sandbox";
+		if (sethostname(name, sizeof(name) - 1))
+			/* silence gcc warning */;
+	}
+#endif
+
+	if (opt_use_ns_net)
+		ns_net_setup();
+
+	if (opt_use_ns_mnt)
+		ns_mount_setup();
+
+	if (opt_use_ns_mnt && opt_use_ns_pid)
+		pid = ns_pid_setup();
+	else
+		pid = fork();
+
+	if (opt_use_ns_user)
+		ns_user_switch(0, 0, uid, gid);
+
+	return pid;
+}
+
+#endif

diff --git a/src/options.c b/src/options.c
index 10f937c..295ee75 100644
--- a/src/options.c
+++ b/src/options.c
@@ -9,6 +9,43 @@
 #include "sbutil.h"
 #include "sandbox.h"
 
+/* Setting to -1 will load defaults from the config file. */
+int opt_use_namespaces = -1;
+int opt_use_ns_ipc = -1;
+int opt_use_ns_mnt = -1;
+int opt_use_ns_net = -1;
+int opt_use_ns_pid = -1;
+int opt_use_ns_sysv = -1;
+int opt_use_ns_user = -1;
+int opt_use_ns_uts = -1;
+
+static const struct {
+	const char *name;
+	int *opt;
+	int default_val;
+} config_opts[] = {
+	/* Default these to off until they can get more testing. */
+	{ "NAMESPACES_ENABLE",     &opt_use_namespaces, false, },
+	{ "NAMESPACE_IPC_ENABLE",  &opt_use_ns_ipc,     false, },
+	{ "NAMESPACE_MNT_ENABLE",  &opt_use_ns_mnt,     false, },
+	{ "NAMESPACE_NET_ENABLE",  &opt_use_ns_net,     false, },
+	{ "NAMESPACE_PID_ENABLE",  &opt_use_ns_pid,     false, },
+	{ "NAMESPACE_SYSV_ENABLE", &opt_use_ns_sysv,    false, },
+	{ "NAMESPACE_USER_ENABLE", &opt_use_ns_user,    false, },
+	{ "NAMESPACE_UTS_ENABLE",  &opt_use_ns_uts,     false, },
+};
+
+static void read_config(void)
+{
+	size_t i;
+
+	for (i = 0; i < ARRAY_SIZE(config_opts); ++i) {
+		int *opt = config_opts[i].opt;
+		if (*opt == -1)
+			*opt = sb_get_cnf_bool(config_opts[i].name, config_opts[i].default_val);
+	}
+}
+
 static void show_version(void)
 {
 	puts(
@@ -36,11 +73,43 @@ static void show_version(void)
 #define PARSE_FLAGS "+hV"
 #define a_argument required_argument
 static struct option const long_opts[] = {
+	{"ns-on",         no_argument, &opt_use_namespaces, true},
+	{"ns-off",        no_argument, &opt_use_namespaces, false},
+	{"ns-ipc-on",     no_argument, &opt_use_ns_ipc, true},
+	{"ns-ipc-off",    no_argument, &opt_use_ns_ipc, false},
+	{"ns-mnt-on",     no_argument, &opt_use_ns_mnt, true},
+	{"ns-mnt-off",    no_argument, &opt_use_ns_mnt, false},
+	{"ns-net-on",     no_argument, &opt_use_ns_net, true},
+	{"ns-net-off",    no_argument, &opt_use_ns_net, false},
+	{"ns-pid-on",     no_argument, &opt_use_ns_pid, true},
+	{"ns-pid-off",    no_argument, &opt_use_ns_pid, false},
+	{"ns-sysv-on",    no_argument, &opt_use_ns_sysv, true},
+	{"ns-sysv-off",   no_argument, &opt_use_ns_sysv, false},
+	{"ns-user-on",    no_argument, &opt_use_ns_user, true},
+	{"ns-user-off",   no_argument, &opt_use_ns_user, false},
+	{"ns-uts-on",     no_argument, &opt_use_ns_uts, true},
+	{"ns-uts-off",    no_argument, &opt_use_ns_uts, false},
 	{"help",          no_argument, NULL, 'h'},
 	{"version",       no_argument, NULL, 'V'},
 	{NULL,            no_argument, NULL, 0x0}
 };
 static const char * const opts_help[] = {
+	"Enable  the use of namespaces",
+	"Disable the use of namespaces",
+	"Enable  the use of IPC (and System V) namespaces",
+	"Disable the use of IPC (and System V) namespaces",
+	"Enable  the use of mount namespaces",
+	"Disable the use of mount namespaces",
+	"Enable  the use of network namespaces",
+	"Disable the use of network namespaces",
+	"Enable  the use of process (pid) namespaces",
+	"Disable the use of process (pid) namespaces",
+	"Enable  the use of System V namespaces",
+	"Disable the use of System V namespaces",
+	"Enable  the use of user namespaces",
+	"Disable the use of user namespaces",
+	"Enable  the use of UTS (hostname/uname) namespaces",
+	"Disable the use of UTS (hostname/uname) namespaces",
 	"Print this help and exit",
 	"Print version and exit",
 	NULL
@@ -113,4 +182,6 @@ void parseargs(int argc, char *argv[])
 			show_usage(1);
 		}
 	}
+
+	read_config();
 }

diff --git a/src/sandbox.c b/src/sandbox.c
index 15c87b2..c668ab6 100644
--- a/src/sandbox.c
+++ b/src/sandbox.c
@@ -160,7 +160,7 @@ static int spawn_shell(char *argv_bash[], char **env, int debug)
 	int status = 0;
 	int ret = 0;
 
-	child_pid = fork();
+	child_pid = opt_use_namespaces ? setup_namespaces() : fork();
 
 	/* Child's process */
 	if (0 == child_pid) {

diff --git a/src/sandbox.h b/src/sandbox.h
index 4233bd6..303dac4 100644
--- a/src/sandbox.h
+++ b/src/sandbox.h
@@ -28,6 +28,12 @@ extern char **setup_environ(struct sandbox_info_t *sandbox_info);
 
 extern bool sb_get_cnf_bool(const char *, bool);
 
+#ifdef __linux__
+extern pid_t setup_namespaces(void);
+#else
+#define setup_namespaces() fork()
+#endif
+
 #define sb_warn(fmt, args...)  fprintf(stderr, "%s:%s  " fmt "\n", "sandbox", __func__, ## args)
 #define sb_pwarn(fmt, args...) sb_warn(fmt ": %s\n", ## args, strerror(errno))
 #define _sb_err(func, fmt, args...) do { sb_##func(fmt, ## args); exit(EXIT_FAILURE); } while (0)
@@ -36,5 +42,13 @@ extern bool sb_get_cnf_bool(const char *, bool);
 
 /* Option parsing related code */
 extern void parseargs(int argc, char *argv[]);
+extern int opt_use_namespaces;
+extern int opt_use_ns_ipc;
+extern int opt_use_ns_mnt;
+extern int opt_use_ns_net;
+extern int opt_use_ns_pid;
+extern int opt_use_ns_sysv;
+extern int opt_use_ns_user;
+extern int opt_use_ns_uts;
 
 #endif


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2015-09-27  6:27 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-09-27  6:27 [gentoo-commits] proj/sandbox:master commit in: /, src/, etc/ Mike Frysinger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox