aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac5
-rw-r--r--etc/sandbox.conf20
-rw-r--r--headers.h12
-rw-r--r--src/Makefile.am1
-rw-r--r--src/namespaces.c219
-rw-r--r--src/options.c71
-rw-r--r--src/sandbox.c2
-rw-r--r--src/sandbox.h14
8 files changed, 343 insertions, 1 deletions
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