| Index: chrome/browser/zygote_main_linux.cc
|
| ===================================================================
|
| --- chrome/browser/zygote_main_linux.cc (revision 30499)
|
| +++ chrome/browser/zygote_main_linux.cc (working copy)
|
| @@ -3,18 +3,25 @@
|
| // found in the LICENSE file.
|
|
|
| #include <dlfcn.h>
|
| -#include <unistd.h>
|
| #include <sys/epoll.h>
|
| +#include <sys/prctl.h>
|
| +#include <sys/signal.h>
|
| +#include <sys/socket.h>
|
| #include <sys/types.h>
|
| -#include <sys/socket.h>
|
| -#include <sys/signal.h>
|
| -#include <sys/prctl.h>
|
| #include <sys/wait.h>
|
| +#include <unistd.h>
|
|
|
| +#if defined(CHROMIUM_SELINUX)
|
| +#include <selinux/selinux.h>
|
| +#include <selinux/context.h>
|
| +#endif
|
| +
|
| #include "base/basictypes.h"
|
| #include "base/command_line.h"
|
| #include "base/eintr_wrapper.h"
|
| #include "base/global_descriptors_posix.h"
|
| +#include "base/hash_tables.h"
|
| +#include "base/linux_util.h"
|
| #include "base/path_service.h"
|
| #include "base/pickle.h"
|
| #include "base/rand_util.h"
|
| @@ -33,16 +40,14 @@
|
|
|
| #include "skia/ext/SkFontHost_fontconfig_control.h"
|
|
|
| -#if defined(CHROMIUM_SELINUX)
|
| -#include <selinux/selinux.h>
|
| -#include <selinux/context.h>
|
| -#endif
|
| -
|
| #include "unicode/timezone.h"
|
|
|
| // http://code.google.com/p/chromium/wiki/LinuxZygote
|
|
|
| +static const int kBrowserDescriptor = 3;
|
| static const int kMagicSandboxIPCDescriptor = 5;
|
| +static const int kZygoteIdDescriptor = 7;
|
| +static bool g_suid_sandbox_active = false;
|
|
|
| // This is the object which implements the zygote. The ZygoteMain function,
|
| // which is called from ChromeMain, at the the bottom and simple constructs one
|
| @@ -52,7 +57,7 @@
|
| bool ProcessRequests() {
|
| // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the
|
| // browser on it.
|
| - // A SOCK_DGRAM is installed in fd 4. This is the sandbox IPC channel.
|
| + // A SOCK_DGRAM is installed in fd 5. This is the sandbox IPC channel.
|
| // See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC
|
|
|
| // We need to accept SIGCHLD, even though our handler is a no-op because
|
| @@ -62,8 +67,17 @@
|
| action.sa_handler = SIGCHLDHandler;
|
| CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
|
|
|
| + if (g_suid_sandbox_active) {
|
| + // Let the ZygoteHost know we are ready to go.
|
| + // The receiving code is in chrome/browser/zygote_host_linux.cc.
|
| + std::vector<int> empty;
|
| + bool r = base::SendMsg(kBrowserDescriptor, kZygoteMagic,
|
| + sizeof(kZygoteMagic), empty);
|
| + CHECK(r) << "Sending zygote magic failed";
|
| + }
|
| +
|
| for (;;) {
|
| - if (HandleRequestFromBrowser(3))
|
| + if (HandleRequestFromBrowser(kBrowserDescriptor))
|
| return true;
|
| }
|
| }
|
| @@ -122,20 +136,30 @@
|
| return false;
|
| }
|
|
|
| - bool HandleReapRequest(int fd, Pickle& pickle, void* iter) {
|
| - pid_t child;
|
| + bool HandleReapRequest(int fd, const Pickle& pickle, void* iter) {
|
| + base::ProcessId child;
|
| + base::ProcessId actual_child;
|
|
|
| if (!pickle.ReadInt(&iter, &child)) {
|
| LOG(WARNING) << "Error parsing reap request from browser";
|
| return false;
|
| }
|
|
|
| - ProcessWatcher::EnsureProcessTerminated(child);
|
| + if (g_suid_sandbox_active) {
|
| + actual_child = real_pids_to_sandbox_pids[child];
|
| + if (!actual_child)
|
| + return false;
|
| + real_pids_to_sandbox_pids.erase(child);
|
| + } else {
|
| + actual_child = child;
|
| + }
|
|
|
| + ProcessWatcher::EnsureProcessTerminated(actual_child);
|
| +
|
| return false;
|
| }
|
|
|
| - bool HandleDidProcessCrash(int fd, Pickle& pickle, void* iter) {
|
| + bool HandleDidProcessCrash(int fd, const Pickle& pickle, void* iter) {
|
| base::ProcessHandle child;
|
|
|
| if (!pickle.ReadInt(&iter, &child)) {
|
| @@ -144,7 +168,13 @@
|
| }
|
|
|
| bool child_exited;
|
| - bool did_crash = base::DidProcessCrash(&child_exited, child);
|
| + bool did_crash;
|
| + if (g_suid_sandbox_active)
|
| + child = real_pids_to_sandbox_pids[child];
|
| + if (child)
|
| + did_crash = base::DidProcessCrash(&child_exited, child);
|
| + else
|
| + did_crash = child_exited = false;
|
|
|
| Pickle write_pickle;
|
| write_pickle.WriteBool(did_crash);
|
| @@ -156,12 +186,14 @@
|
|
|
| // Handle a 'fork' request from the browser: this means that the browser
|
| // wishes to start a new renderer.
|
| - bool HandleForkRequest(int fd, Pickle& pickle, void* iter,
|
| + bool HandleForkRequest(int fd, const Pickle& pickle, void* iter,
|
| std::vector<int>& fds) {
|
| std::vector<std::string> args;
|
| int argc, numfds;
|
| base::GlobalDescriptors::Mapping mapping;
|
| - pid_t child;
|
| + base::ProcessId child;
|
| + uint64_t dummy_inode = 0;
|
| + int dummy_fd = -1;
|
|
|
| if (!pickle.ReadInt(&iter, &argc))
|
| goto error;
|
| @@ -186,12 +218,22 @@
|
| }
|
|
|
| mapping.push_back(std::make_pair(
|
| - static_cast<uint32_t>(kSandboxIPCChannel), 5));
|
| + static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor));
|
|
|
| + if (g_suid_sandbox_active) {
|
| + dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
|
| + if (dummy_fd < 0)
|
| + goto error;
|
| +
|
| + if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd))
|
| + goto error;
|
| + }
|
| +
|
| child = fork();
|
|
|
| if (!child) {
|
| - close(3); // our socket from the browser is in fd 3
|
| + close(kBrowserDescriptor); // our socket from the browser
|
| + close(kZygoteIdDescriptor); // another socket from the browser
|
| Singleton<base::GlobalDescriptors>()->Reset(mapping);
|
|
|
| // Reset the process-wide command line to our new command line.
|
| @@ -200,22 +242,59 @@
|
| CommandLine::ForCurrentProcess()->InitFromArgv(args);
|
| CommandLine::SetProcTitle();
|
| return true;
|
| + } else if (child < 0) {
|
| + LOG(ERROR) << "Zygote could not fork";
|
| + goto error;
|
| }
|
|
|
| - for (std::vector<int>::const_iterator
|
| - i = fds.begin(); i != fds.end(); ++i)
|
| - close(*i);
|
| + {
|
| + base::ProcessId proc_id;
|
| + if (g_suid_sandbox_active) {
|
| + close(dummy_fd);
|
| + dummy_fd = -1;
|
| + uint8_t reply_buf[512];
|
| + Pickle request;
|
| + request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE);
|
| + request.WriteUInt64(dummy_inode);
|
|
|
| - HANDLE_EINTR(write(fd, &child, sizeof(child)));
|
| - return false;
|
| + const ssize_t r = base::SendRecvMsg(kMagicSandboxIPCDescriptor,
|
| + reply_buf, sizeof(reply_buf),
|
| + NULL, request);
|
| + if (r == -1)
|
| + goto error;
|
|
|
| + Pickle reply(reinterpret_cast<char*>(reply_buf), r);
|
| + void* iter2 = NULL;
|
| + if (!reply.ReadInt(&iter2, &proc_id))
|
| + goto error;
|
| + real_pids_to_sandbox_pids[proc_id] = child;
|
| + } else {
|
| + proc_id = child;
|
| + }
|
| +
|
| + for (std::vector<int>::const_iterator
|
| + i = fds.begin(); i != fds.end(); ++i)
|
| + close(*i);
|
| +
|
| + HANDLE_EINTR(write(fd, &proc_id, sizeof(proc_id)));
|
| + return false;
|
| + }
|
| +
|
| error:
|
| - LOG(WARNING) << "Error parsing fork request from browser";
|
| + LOG(ERROR) << "Error parsing fork request from browser";
|
| for (std::vector<int>::const_iterator
|
| i = fds.begin(); i != fds.end(); ++i)
|
| close(*i);
|
| + if (dummy_fd >= 0)
|
| + close(dummy_fd);
|
| return false;
|
| }
|
| +
|
| + // In the SUID sandbox, we try to use a new PID namespace. Thus the PIDs
|
| + // fork() returns are not the real PIDs, so we need to map the Real PIDS
|
| + // into the sandbox PID namespace.
|
| + typedef base::hash_map<base::ProcessHandle, base::ProcessHandle> ProcessMap;
|
| + ProcessMap real_pids_to_sandbox_pids;
|
| };
|
|
|
| // With SELinux we can carve out a precise sandbox, so we don't have to play
|
| @@ -402,6 +481,8 @@
|
| // over which we can signal that we have completed our startup and can be
|
| // chrooted.
|
|
|
| + g_suid_sandbox_active = true;
|
| +
|
| char* endptr;
|
| const long fd_long = strtol(sandbox_fd_string, &endptr, 10);
|
| if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX)
|
|
|