aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/main/tools/namespace-sandbox.c230
1 files changed, 126 insertions, 104 deletions
diff --git a/src/main/tools/namespace-sandbox.c b/src/main/tools/namespace-sandbox.c
index 4a1a2bd7e6..5cf6b433be 100644
--- a/src/main/tools/namespace-sandbox.c
+++ b/src/main/tools/namespace-sandbox.c
@@ -34,7 +34,6 @@
#include <unistd.h>
static int global_debug = 0;
-static int global_cpid; // Returned by fork()
#define PRINT_DEBUG(...) do { if (global_debug) {fprintf(stderr, "sandbox.c: " __VA_ARGS__);}} while(0)
@@ -42,7 +41,7 @@ static int global_cpid; // Returned by fork()
#define CHECK_NOT_NULL(x) if (x == NULL) { perror(#x); exit(1); }
#define DIE() do { fprintf(stderr, "Error in %d\n", __LINE__); exit(-1); } while(0);
-int kChildrenCleanupDelay = 1;
+const int kChildrenCleanupDelay = 1;
static volatile sig_atomic_t global_signal_received = 0;
@@ -50,15 +49,15 @@ static volatile sig_atomic_t global_signal_received = 0;
// Options parsing result
//
struct Options {
- char *include_prefix;
- char *sandbox_root;
- char *tools;
- char **mounts;
- char **includes;
- int num_mounts;
- int num_includes;
- int timeout;
- char **args;
+ char **args; // Command to run (-C / --)
+ char *include_prefix; // Include prefix (-N)
+ char *sandbox_root; // Sandbox root (-S)
+ char *tools; // tools directory (-t)
+ char **mounts; // List of directories to mount (-m)
+ char **includes; // List of include directories (-n)
+ int num_mounts; // size of mounts
+ int num_includes; // size of includes
+ int timeout; // Timeout (-T)
};
// Print out a usage error. argc and argv are the argument counter
@@ -71,14 +70,20 @@ void ParseCommandLine(int argc, char **argv, struct Options *opt);
// Signal hanlding
void PropagateSignals();
void EnableAlarm();
+// Sandbox setup
+void SetupDirectories(struct Options* opt);
void SetupSlashDev();
+void SetupUserNamespace(int uid, int gid);
+void ChangeRoot();
// Write the file "filename" using a format string specified by "fmt".
// Returns -1 on failure.
int WriteFile(const char *filename, const char *fmt, ...);
+// Run the command specified by the argv array and kill it after
+// timeout seconds.
+void SpawnCommand(char **argv, int timeout);
+
+
-//
-// Main method
-//
int main(int argc, char *argv[]) {
struct Options opt = {
.args = NULL,
@@ -100,80 +105,28 @@ int main(int argc, char *argv[]) {
// create new namespaces in which this process and its children will live
CHECK_CALL(unshare(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER));
CHECK_CALL(mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL));
- // mount sandbox and go there
- CHECK_CALL(mount(opt.sandbox_root, opt.sandbox_root, NULL, MS_BIND | MS_NOSUID, NULL));
- CHECK_CALL(chdir(opt.sandbox_root));
-
- SetupSlashDev();
- // mount blaze specific directories - tools/ and build-runfiles/
- if (opt.tools != NULL) {
- PRINT_DEBUG("tools: %s\n", opt.tools);
- CHECK_CALL(mkdir("tools", 0755));
- CHECK_CALL(mount(opt.tools, "tools", NULL, MS_BIND | MS_RDONLY, NULL));
- }
-
- // mounts passed in argv; those are mostly dirs for shared libs
- for (int i = 0; i < opt.num_mounts; i++) {
- CHECK_CALL(mount(opt.mounts[i], opt.mounts[i] + 1, NULL, MS_BIND | MS_RDONLY, NULL));
- }
-
- // c++ compilation
- // headers go in separate directory
- if (opt.include_prefix != NULL) {
- CHECK_CALL(chdir(opt.include_prefix));
- for (int i = 0; i < opt.num_includes; i++) {
- // TODO(bazel-team) sometimes list of -iquote given by bazel contains
- // invalid (non-existing) entries, ideally we would like not to have them
- PRINT_DEBUG("include: %s\n", opt.includes[i]);
- if (mount(opt.includes[i], opt.includes[i] + 1 , NULL, MS_BIND, NULL) > -1) {
- continue;
- }
- if (errno == ENOENT) {
- continue;
- }
- CHECK_CALL(-1);
- }
- CHECK_CALL(chdir(".."));
- }
-
- // Disable needs for CAP_SETGID
- int r = WriteFile("/proc/self/setgroups", "deny");
- if (r < 0 && errno != ENOENT) {
- // Writing to /proc/self/setgroups might fail on earlier
- // version of linux because setgroups does not exist, ignore.
- perror("WriteFile(\"/proc/self/setgroups\", \"deny\")");
- exit(-1);
- }
- // set group and user mapping from outer namespace to inner:
- // no changes in the parent, be root in the child
- CHECK_CALL(WriteFile("/proc/self/uid_map", "0 %d 1\n", uid));
- CHECK_CALL(WriteFile("/proc/self/gid_map", "0 %d 1\n", gid));
-
- CHECK_CALL(setresuid(0, 0, 0));
- CHECK_CALL(setresgid(0, 0, 0));
-
- CHECK_CALL(mkdir("proc", 0755));
- CHECK_CALL(mount("/proc", "proc", NULL, MS_REC | MS_BIND, NULL));
+ // Create the sandbox directory layout
+ SetupDirectories(&opt);
+ // Set the user namespace (user_namespaces(7))
+ SetupUserNamespace(uid, gid);
// make sandbox actually hermetic:
- // move the real root to old_root, then detach it
- char old_root[16] = "old-root-XXXXXX";
- CHECK_NOT_NULL(mkdtemp(old_root));
- // pivot_root has no wrapper in libc, so we need syscall()
- CHECK_CALL(syscall(SYS_pivot_root, ".", old_root));
- CHECK_CALL(chroot("."));
- CHECK_CALL(umount2(old_root, MNT_DETACH));
- CHECK_CALL(rmdir(old_root));
+ ChangeRoot();
+ // Finally call the command
free(opt.mounts);
free(opt.includes);
+ SpawnCommand(opt.args, opt.timeout);
+ return 0;
+}
- for (int i = 0; opt.args[i] != NULL; i += 1) {
- PRINT_DEBUG("arg: %s\n", opt.args[i]);
+void SpawnCommand(char **argv, int timeout) {
+ for (int i = 0; argv[i] != NULL; i++) {
+ PRINT_DEBUG("arg: %s\n", argv[i]);
}
// spawn child and wait until it finishes
- global_cpid = fork();
- if (global_cpid == 0) {
+ pid_t cpid = fork();
+ if (cpid == 0) {
CHECK_CALL(setpgid(0, 0));
// if the execvp below fails with "No such file or directory" it means that:
// a) the binary is not in the sandbox (which means it wasn't included in
@@ -184,7 +137,7 @@ int main(int argc, char *argv[]) {
// c) the binary uses elf interpreter which is not inside sandbox - you can
// check for that by running "readelf -a a.out | grep interpreter" (the
// sandbox code assumes that it is either in /lib*/ or /usr/lib*/)
- CHECK_CALL(execvp(opt.args[0], opt.args));
+ CHECK_CALL(execvp(argv[0], argv));
PRINT_DEBUG("Exec failed near %s:%d\n", __FILE__, __LINE__);
exit(1);
} else {
@@ -193,17 +146,17 @@ int main(int argc, char *argv[]) {
// entire sandbox)
PropagateSignals();
// after given timeout, kill children
- EnableAlarm(opt.timeout);
+ EnableAlarm(timeout);
int status = 0;
while (1) {
PRINT_DEBUG("Waiting for the child...\n");
pid_t pid = wait(&status);
if (global_signal_received) {
PRINT_DEBUG("Received signal: %s\n", strsignal(global_signal_received));
- CHECK_CALL(killpg(global_cpid, global_signal_received));
+ CHECK_CALL(killpg(cpid, global_signal_received));
// give children some time for cleanup before they terminate
sleep(kChildrenCleanupDelay);
- CHECK_CALL(killpg(global_cpid, SIGKILL));
+ CHECK_CALL(killpg(cpid, SIGKILL));
exit(128 | global_signal_received);
}
if (errno == EINTR) {
@@ -226,8 +179,22 @@ int main(int argc, char *argv[]) {
}
}
}
+}
- return 0;
+int WriteFile(const char *filename, const char *fmt, ...) {
+ int r;
+ va_list ap;
+ FILE *stream = fopen(filename, "w");
+ if (stream == NULL) {
+ return -1;
+ }
+ va_start(ap, fmt);
+ r = vfprintf(stream, fmt, ap);
+ va_end(ap);
+ if (r >= 0) {
+ r = fclose(stream);
+ }
+ return r;
}
//
@@ -256,6 +223,17 @@ void PropagateSignals() {
}
}
+void EnableAlarm(int timeout) {
+ if (timeout <= 0) return;
+
+ struct itimerval timer = {};
+ timer.it_value.tv_sec = (long) timeout;
+ CHECK_CALL(setitimer(ITIMER_REAL, &timer, NULL));
+}
+
+//
+// Sandbox setup
+//
void SetupSlashDev() {
CHECK_CALL(mkdir("dev", 0755));
const char *devs[] = {
@@ -275,29 +253,73 @@ void SetupSlashDev() {
}
}
-void EnableAlarm(int timeout) {
- if (timeout <= 0) return;
+void SetupDirectories(struct Options *opt) {
+ // Mount the sandbox and go there.
+ CHECK_CALL(mount(opt->sandbox_root, opt->sandbox_root, NULL, MS_BIND | MS_NOSUID, NULL));
+ CHECK_CALL(chdir(opt->sandbox_root));
+ SetupSlashDev();
+ // Mount blaze specific directories - tools/ and build-runfiles/.
+ if (opt->tools != NULL) {
+ PRINT_DEBUG("tools: %s\n", opt->tools);
+ CHECK_CALL(mkdir("tools", 0755));
+ CHECK_CALL(mount(opt->tools, "tools", NULL, MS_BIND | MS_RDONLY, NULL));
+ }
- struct itimerval timer = {};
- timer.it_value.tv_sec = (long) timeout;
- CHECK_CALL(setitimer(ITIMER_REAL, &timer, NULL));
-}
+ // Mount directories passed in argv; those are mostly dirs for shared libs.
+ for (int i = 0; i < opt->num_mounts; i++) {
+ CHECK_CALL(mount(opt->mounts[i], opt->mounts[i] + 1, NULL, MS_BIND | MS_RDONLY, NULL));
+ }
-int WriteFile(const char *filename, const char *fmt, ...) {
- FILE *stream;
- int r;
- va_list ap;
- stream = fopen(filename, "w");
- if (stream == NULL) {
- return -1;
+ // C++ compilation
+ // C++ headers go in a separate directory.
+ if (opt->include_prefix != NULL) {
+ CHECK_CALL(chdir(opt->include_prefix));
+ for (int i = 0; i < opt->num_includes; i++) {
+ // TODO(bazel-team): sometimes list of -iquote given by bazel contains
+ // invalid (non-existing) entries, ideally we would like not to have them
+ PRINT_DEBUG("include: %s\n", opt->includes[i]);
+ if (mount(opt->includes[i], opt->includes[i] + 1 , NULL, MS_BIND, NULL) > -1) {
+ continue;
+ }
+ if (errno == ENOENT) {
+ continue;
+ }
+ CHECK_CALL(-1);
+ }
+ CHECK_CALL(chdir(".."));
}
- va_start(ap, fmt);
- r = vfprintf(stream, fmt, ap);
- va_end(ap);
- if (r >= 0) {
- r = fclose(stream);
+
+ CHECK_CALL(mkdir("proc", 0755));
+ CHECK_CALL(mount("/proc", "proc", NULL, MS_REC | MS_BIND, NULL));
+}
+
+void SetupUserNamespace(int uid, int gid) {
+ // Disable needs for CAP_SETGID
+ int r = WriteFile("/proc/self/setgroups", "deny");
+ if (r < 0 && errno != ENOENT) {
+ // Writing to /proc/self/setgroups might fail on earlier
+ // version of linux because setgroups does not exist, ignore.
+ perror("WriteFile(\"/proc/self/setgroups\", \"deny\")");
+ exit(-1);
}
- return r;
+ // set group and user mapping from outer namespace to inner:
+ // no changes in the parent, be root in the child
+ CHECK_CALL(WriteFile("/proc/self/uid_map", "0 %d 1\n", uid));
+ CHECK_CALL(WriteFile("/proc/self/gid_map", "0 %d 1\n", gid));
+
+ CHECK_CALL(setresuid(0, 0, 0));
+ CHECK_CALL(setresgid(0, 0, 0));
+}
+
+void ChangeRoot() {
+ // move the real root to old_root, then detach it
+ char old_root[16] = "old-root-XXXXXX";
+ CHECK_NOT_NULL(mkdtemp(old_root));
+ // pivot_root has no wrapper in libc, so we need syscall()
+ CHECK_CALL(syscall(SYS_pivot_root, ".", old_root));
+ CHECK_CALL(chroot("."));
+ CHECK_CALL(umount2(old_root, MNT_DETACH));
+ CHECK_CALL(rmdir(old_root));
}
//