diff options
Diffstat (limited to 'sandbox/linux/suid/sandbox.c')
-rw-r--r-- | sandbox/linux/suid/sandbox.c | 180 |
1 files changed, 132 insertions, 48 deletions
diff --git a/sandbox/linux/suid/sandbox.c b/sandbox/linux/suid/sandbox.c index a545208..475378c 100644 --- a/sandbox/linux/suid/sandbox.c +++ b/sandbox/linux/suid/sandbox.c @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -26,6 +26,7 @@ #include <sys/vfs.h> #include <unistd.h> +#include "init_process.h" #include "linux_util.h" #include "process_util.h" #include "suid_unsafe_environment_variables.h" @@ -56,7 +57,7 @@ static void FatalError(const char *msg, ...) { vfprintf(stderr, msg, ap); fprintf(stderr, ": %s\n", strerror(errno)); fflush(stderr); - exit(1); + _exit(1); } // We will chroot() to the helper's /proc/self directory. Anything there will @@ -70,11 +71,47 @@ static void FatalError(const char *msg, ...) { #define SAFE_DIR "/proc/self/fdinfo" #define SAFE_DIR2 "/proc/self/fd" -static bool SpawnChrootHelper() { +static bool DropRoot() { + if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) { + perror("prctl(PR_SET_DUMPABLE)"); + return false; + } + + if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { + perror("Still dumpable after prctl(PR_SET_DUMPABLE)"); + return false; + } + + gid_t rgid, egid, sgid; + if (getresgid(&rgid, &egid, &sgid)) { + perror("getresgid"); + return false; + } + + if (setresgid(rgid, rgid, rgid)) { + perror("setresgid"); + return false; + } + + uid_t ruid, euid, suid; + if (getresuid(&ruid, &euid, &suid)) { + perror("getresuid"); + return false; + } + + if (setresuid(ruid, ruid, ruid)) { + perror("setresuid"); + return false; + } + + return true; +} + +static int SpawnChrootHelper() { int sv[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) { perror("socketpair"); - return false; + return -1; } char *safedir = NULL; @@ -86,7 +123,7 @@ static bool SpawnChrootHelper() { safedir = SAFE_DIR2; else { fprintf(stderr, "Could not find %s\n", SAFE_DIR2); - return false; + return -1; } const pid_t pid = syscall( @@ -96,7 +133,7 @@ static bool SpawnChrootHelper() { perror("clone"); close(sv[0]); close(sv[1]); - return false; + return -1; } if (pid == 0) { @@ -124,6 +161,7 @@ static bool SpawnChrootHelper() { FatalError("read"); // do chrooting + errno = 0; if (msg != kMsgChrootMe) FatalError("Unknown message from sandboxed process"); @@ -156,7 +194,7 @@ static bool SpawnChrootHelper() { if (close(sv[0])) { close(sv[1]); perror("close"); - return false; + return -1; } // In the parent process, we install an environment variable containing the @@ -165,13 +203,14 @@ static bool SpawnChrootHelper() { int printed = snprintf(desc_str, sizeof(desc_str), "%u", sv[1]); if (printed < 0 || printed >= (int)sizeof(desc_str)) { fprintf(stderr, "Failed to snprintf\n"); - return false; + close(sv[1]); + return -1; } if (setenv(kSandboxDescriptorEnvironmentVarName, desc_str, 1)) { perror("setenv"); close(sv[1]); - return false; + return -1; } // We also install an environment variable containing the pid of the child @@ -179,15 +218,51 @@ static bool SpawnChrootHelper() { printed = snprintf(helper_pid_str, sizeof(helper_pid_str), "%u", pid); if (printed < 0 || printed >= (int)sizeof(helper_pid_str)) { fprintf(stderr, "Failed to snprintf\n"); - return false; + close(sv[1]); + return -1; } if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) { perror("setenv"); close(sv[1]); - return false; + return -1; } + return sv[1]; +} + +static bool JailMe() { + int fd = SpawnChrootHelper(); + if (fd < 0) { + return false; + } + if (!DropRoot()) { + close(fd); + return false; + } + ssize_t bytes; + char ch = kMsgChrootMe; + do { + errno = 0; + bytes = write(fd, &ch, 1); + } while (bytes == -1 && errno == EINTR); + if (bytes != 1) { + perror("write"); + close(fd); + return false; + } + do { + errno = 0; + bytes = read(fd, &ch, 1); + } while (bytes == -1 && errno == EINTR); + close(fd); + if (bytes != 1) { + perror("read"); + return false; + } + if (ch != kMsgChrootSuccessful) { + return false; + } return true; } @@ -207,6 +282,51 @@ static bool MoveToNewNamespaces() { _exit(0); if (pid == 0) { + if (syscall(__NR_getpid) == 1) { + int fds[2]; + char ch = 0; + if (pipe(fds)) { + perror("Failed to create pipe"); + _exit(1); + } + pid = fork(); + if (pid > 0) { + // The very first process in the new namespace takes on the + // role of the traditional "init" process. It must reap exit + // codes of daemon processes until the namespace is completely + // empty. + // We have to be careful that this "init" process doesn't + // provide a new attack surface. So, we also move it into + // a separate chroot and we drop all privileges. It does + // still need to access "/proc" and "/dev/null", though. So, + // we have to provide it with a file handles to these resources. + // These file handle are not accessible by any other processes in + // the sandbox and thus safe. + close(fds[0]); + int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY); + int null_fd = open("/dev/null", O_RDWR); + if (!JailMe()) { + FatalError("Could not remove privileges from " + "new \"init\" process"); + } + SystemInitProcess(fds[1], pid, proc_fd, null_fd); + } else if (pid != 0) { + perror("Failed to fork"); + _exit(1); + } + // Wait for the "init" process to complete initialization. + close(fds[1]); + errno = 0; + while (read(fds[0], &ch, 1) < 0 && errno == EINTR) { + } + close(fds[0]); + if (ch != ' ') { + // We'll likely never get here. If the "init" process fails, it's + // death typically takes everyone of its children with it. + FatalError("Failed to set up new \"init\" process inside sandbox"); + } + } + if (kCloneExtraFlags[i] & CLONE_NEWPID) { setenv("SBX_PID_NS", "", 1 /* overwrite */); } else { @@ -232,42 +352,6 @@ static bool MoveToNewNamespaces() { return true; } -static bool DropRoot() { - if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0)) { - perror("prctl(PR_SET_DUMPABLE)"); - return false; - } - - if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) { - perror("Still dumpable after prctl(PR_SET_DUMPABLE)"); - return false; - } - - gid_t rgid, egid, sgid; - if (getresgid(&rgid, &egid, &sgid)) { - perror("getresgid"); - return false; - } - - if (setresgid(rgid, rgid, rgid)) { - perror("setresgid"); - return false; - } - - uid_t ruid, euid, suid; - if (getresuid(&ruid, &euid, &suid)) { - perror("getresuid"); - return false; - } - - if (setresuid(ruid, ruid, ruid)) { - perror("setresuid"); - return false; - } - - return true; -} - static bool SetupChildEnvironment() { unsigned i; @@ -343,7 +427,7 @@ int main(int argc, char **argv) { if (!MoveToNewNamespaces()) return 1; - if (!SpawnChrootHelper()) + if (SpawnChrootHelper() < 0) return 1; if (!DropRoot()) return 1; |